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 (4 * 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 /* forward declaration for internal use */
82 static int MovingOrBlocked2Element_MM(int, int);
83 static void Bang_MM(int, int);
84 static void RaiseScore_MM(int);
85 static void RemoveMovingField_MM(int, int);
86 static void InitMovingField_MM(int, int, int);
87 static void ContinueMoving_MM(int, int);
88 static void Moving2Blocked_MM(int, int, int *, int *);
91 static int get_element_angle(int element)
93 int element_phase = get_element_phase(element);
95 if (IS_MIRROR_FIXED(element) ||
96 IS_MCDUFFIN(element) ||
99 return 4 * element_phase;
101 return element_phase;
104 static int get_opposite_angle(int angle)
106 int opposite_angle = angle + ANG_RAY_180;
108 /* make sure "opposite_angle" is in valid interval [0, 15] */
109 return (opposite_angle + 16) % 16;
112 static int get_mirrored_angle(int laser_angle, int mirror_angle)
114 int reflected_angle = 16 - laser_angle + mirror_angle;
116 /* make sure "reflected_angle" is in valid interval [0, 15] */
117 return (reflected_angle + 16) % 16;
120 static void InitMovDir_MM(int x, int y)
122 int element = Feld[x][y];
123 static int direction[3][4] =
125 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
126 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
127 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
132 case EL_PACMAN_RIGHT:
136 Feld[x][y] = EL_PACMAN;
137 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
145 static void InitField(int x, int y, boolean init_game)
147 int element = Feld[x][y];
152 Feld[x][y] = EL_EMPTY;
157 if (native_mm_level.auto_count_kettles)
158 game_mm.kettles_still_needed++;
161 case EL_LIGHTBULB_OFF:
162 game_mm.lights_still_needed++;
166 if (IS_MIRROR(element) ||
167 IS_BEAMER_OLD(element) ||
168 IS_BEAMER(element) ||
170 IS_POLAR_CROSS(element) ||
171 IS_DF_MIRROR(element) ||
172 IS_DF_MIRROR_AUTO(element) ||
173 IS_GRID_STEEL_AUTO(element) ||
174 IS_GRID_WOOD_AUTO(element) ||
175 IS_FIBRE_OPTIC(element))
177 if (IS_BEAMER_OLD(element))
179 Feld[x][y] = EL_BEAMER_BLUE_START + (element - EL_BEAMER_START);
180 element = Feld[x][y];
183 if (!IS_FIBRE_OPTIC(element))
185 static int steps_grid_auto = 0;
187 if (game_mm.num_cycle == 0) /* initialize cycle steps for grids */
188 steps_grid_auto = RND(16) * (RND(2) ? -1 : +1);
190 if (IS_GRID_STEEL_AUTO(element) ||
191 IS_GRID_WOOD_AUTO(element))
192 game_mm.cycle[game_mm.num_cycle].steps = steps_grid_auto;
194 game_mm.cycle[game_mm.num_cycle].steps = RND(16) * (RND(2) ? -1 : +1);
196 game_mm.cycle[game_mm.num_cycle].x = x;
197 game_mm.cycle[game_mm.num_cycle].y = y;
201 if (IS_BEAMER(element) || IS_FIBRE_OPTIC(element))
203 int beamer_nr = BEAMER_NR(element);
204 int nr = laser.beamer[beamer_nr][0].num;
206 laser.beamer[beamer_nr][nr].x = x;
207 laser.beamer[beamer_nr][nr].y = y;
208 laser.beamer[beamer_nr][nr].num = 1;
211 else if (IS_PACMAN(element))
215 else if (IS_MCDUFFIN(element) || IS_LASER(element))
217 laser.start_edge.x = x;
218 laser.start_edge.y = y;
219 laser.start_angle = get_element_angle(element);
226 static void InitCycleElements_RotateSingleStep()
230 if (game_mm.num_cycle == 0) /* no elements to cycle */
233 for (i = 0; i < game_mm.num_cycle; i++)
235 int x = game_mm.cycle[i].x;
236 int y = game_mm.cycle[i].y;
237 int step = SIGN(game_mm.cycle[i].steps);
238 int last_element = Feld[x][y];
239 int next_element = get_rotated_element(last_element, step);
241 if (!game_mm.cycle[i].steps)
244 Feld[x][y] = next_element;
247 game_mm.cycle[i].steps -= step;
251 static void InitLaser()
253 int start_element = Feld[laser.start_edge.x][laser.start_edge.y];
254 int step = (IS_LASER(start_element) ? 4 : 0);
256 LX = laser.start_edge.x * TILEX;
257 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
260 LX += (laser.start_angle == ANG_RAY_RIGHT ? 28 + step : 0 - step);
262 LY = laser.start_edge.y * TILEY;
263 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
264 LY += (laser.start_angle == ANG_RAY_DOWN ? 28 + step : 0 - step);
268 XS = 2 * Step[laser.start_angle].x;
269 YS = 2 * Step[laser.start_angle].y;
271 laser.current_angle = laser.start_angle;
273 laser.num_damages = 0;
275 laser.num_beamers = 0;
276 laser.beamer_edge[0] = 0;
278 AddLaserEdge(LX, LY); /* set laser starting edge */
280 pen_ray = GetPixelFromRGB(window,
281 native_mm_level.laser_red * 0xFF,
282 native_mm_level.laser_green * 0xFF,
283 native_mm_level.laser_blue * 0xFF);
286 void InitGameEngine_MM()
290 /* set global game control values */
291 game_mm.num_cycle = 0;
292 game_mm.num_pacman = 0;
295 game_mm.energy_left = 0; // later set to "native_mm_level.time"
296 game_mm.kettles_still_needed =
297 (native_mm_level.auto_count_kettles ? 0 : native_mm_level.kettles_needed);
298 game_mm.lights_still_needed = 0;
299 game_mm.num_keys = 0;
301 game_mm.level_solved = FALSE;
302 game_mm.game_over = FALSE;
303 game_mm.game_over_cause = 0;
305 game_mm.laser_overload_value = 0;
307 /* set global laser control values (must be set before "InitLaser()") */
308 laser.start_edge.x = 0;
309 laser.start_edge.y = 0;
310 laser.start_angle = 0;
312 for (i = 0; i < MAX_NUM_BEAMERS; i++)
313 laser.beamer[i][0].num = laser.beamer[i][1].num = 0;
315 laser.overloaded = FALSE;
316 laser.overload_value = 0;
317 laser.fuse_off = FALSE;
318 laser.fuse_x = laser.fuse_y = -1;
320 laser.dest_element = EL_EMPTY;
325 for (x = 0; x < lev_fieldx; x++)
327 for (y = 0; y < lev_fieldy; y++)
329 Feld[x][y] = Ur[x][y];
330 Hit[x][y] = Box[x][y] = 0;
332 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
333 Store[x][y] = Store2[x][y] = 0;
337 InitField(x, y, TRUE);
342 CloseDoor(DOOR_CLOSE_1);
348 void InitGameActions_MM()
350 int num_init_game_frames = INIT_GAME_ACTIONS_DELAY;
351 int cycle_steps_done = 0;
357 /* copy default game door content to main double buffer */
358 BlitBitmap(pix[PIX_DOOR], drawto,
359 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
363 DrawText(DX_LEVEL, DY_LEVEL,
364 int2str(level_nr, 2), FONT_TEXT_2);
365 DrawText(DX_KETTLES, DY_KETTLES,
366 int2str(game_mm.kettles_still_needed, 3), FONT_TEXT_2);
367 DrawText(DX_SCORE, DY_SCORE,
368 int2str(game_mm.score, 4), FONT_TEXT_2);
377 /* copy actual game door content to door double buffer for OpenDoor() */
378 BlitBitmap(drawto, pix[PIX_DB_DOOR],
379 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
383 OpenDoor(DOOR_OPEN_ALL);
386 for (i = 0; i <= num_init_game_frames; i++)
388 if (i == num_init_game_frames)
389 StopSound_MM(SND_MM_GAME_LEVELTIME_CHARGING);
390 else if (setup.sound_loops)
391 PlaySoundLoop_MM(SND_MM_GAME_LEVELTIME_CHARGING);
393 PlaySound_MM(SND_MM_GAME_LEVELTIME_CHARGING);
395 game_mm.energy_left = native_mm_level.time * i / num_init_game_frames;
397 UpdateAndDisplayGameControlValues();
399 while (cycle_steps_done < NUM_INIT_CYCLE_STEPS * i / num_init_game_frames)
401 InitCycleElements_RotateSingleStep();
411 if (setup.quick_doors)
417 if (setup.sound_music && num_bg_loops)
418 PlayMusic(level_nr % num_bg_loops);
424 void AddLaserEdge(int lx, int ly)
426 if (lx < -2 || ly < -2 || lx >= SXSIZE + 2 || ly >= SYSIZE + 2)
428 Error(ERR_WARN, "AddLaserEdge: out of bounds: %d, %d", lx, ly);
433 laser.edge[laser.num_edges].x = SX + 2 + lx;
434 laser.edge[laser.num_edges].y = SY + 2 + ly;
440 void AddDamagedField(int ex, int ey)
442 laser.damage[laser.num_damages].is_mirror = FALSE;
443 laser.damage[laser.num_damages].angle = laser.current_angle;
444 laser.damage[laser.num_damages].edge = laser.num_edges;
445 laser.damage[laser.num_damages].x = ex;
446 laser.damage[laser.num_damages].y = ey;
456 int last_x = laser.edge[laser.num_edges - 1].x - SX - 2;
457 int last_y = laser.edge[laser.num_edges - 1].y - SY - 2;
459 return ((x - last_x) * XS < 0 || (y - last_y) * YS < 0);
465 static int getMaskFromElement(int element)
467 if (IS_GRID(element))
468 return IMG_MM_MASK_GRID_1 + get_element_phase(element);
469 else if (IS_MCDUFFIN(element))
470 return IMG_MM_MASK_MCDUFFIN_RIGHT + get_element_phase(element);
471 else if (IS_RECTANGLE(element) || IS_DF_GRID(element))
472 return IMG_MM_MASK_RECTANGLE;
474 return IMG_MM_MASK_CIRCLE;
482 printf("ScanPixel: start scanning at (%d, %d) [%d, %d] [%d, %d]\n",
483 LX, LY, LX / TILEX, LY / TILEY, LX % TILEX, LY % TILEY);
486 /* follow laser beam until it hits something (at least the screen border) */
487 while (hit_mask == HIT_MASK_NO_HIT)
493 if (SX + LX < REAL_SX || SX + LX >= REAL_SX + FULL_SXSIZE ||
494 SY + LY < REAL_SY || SY + LY >= REAL_SY + FULL_SYSIZE)
496 printf("ScanPixel: touched screen border!\n");
502 for (i = 0; i < 4; i++)
504 int px = LX + (i % 2) * 2;
505 int py = LY + (i / 2) * 2;
508 int lx = (px + TILEX) / TILEX - 1; /* ...+TILEX...-1 to get correct */
509 int ly = (py + TILEY) / TILEY - 1; /* negative values! */
512 if (IN_LEV_FIELD(lx, ly))
514 int element = Feld[lx][ly];
516 if (element == EL_EMPTY || element == EL_EXPLODING_TRANSP)
520 else if (IS_WALL(element) || IS_WALL_CHANGING(element))
522 int pos = dy / MINI_TILEY * 2 + dx / MINI_TILEX;
524 pixel = ((element & (1 << pos)) ? 1 : 0);
528 int graphic_mask = getMaskFromElement(element);
533 getGraphicSource(graphic_mask, 0, &bitmap, &src_x, &src_y);
538 pixel = (ReadPixel(bitmap, mask_x, mask_y) ? 1 : 0);
543 pixel = (SX + px < REAL_SX || SX + px >= REAL_SX + FULL_SXSIZE ||
544 SY + py < REAL_SY || SY + py >= REAL_SY + FULL_SYSIZE);
547 if ((Sign[laser.current_angle] & (1 << i)) && pixel)
548 hit_mask |= (1 << i);
551 if (hit_mask == HIT_MASK_NO_HIT)
553 /* hit nothing -- go on with another step */
565 int end = 0, rf = laser.num_edges;
567 /* do not scan laser again after the game was lost for whatever reason */
568 if (game_mm.game_over)
571 laser.overloaded = FALSE;
572 laser.stops_inside_element = FALSE;
574 DrawLaser(0, DL_LASER_ENABLED);
577 printf("Start scanning with LX == %d, LY == %d, XS == %d, YS == %d\n",
585 if (laser.num_edges > MAX_LASER_LEN || laser.num_damages > MAX_LASER_LEN)
588 laser.overloaded = TRUE;
593 hit_mask = ScanPixel();
596 printf("Hit something at LX == %d, LY == %d, XS == %d, YS == %d\n",
600 /* hit something -- check out what it was */
601 ELX = (LX + XS) / TILEX;
602 ELY = (LY + YS) / TILEY;
605 printf("hit_mask (1) == '%x' (%d, %d) (%d, %d)\n",
606 hit_mask, LX, LY, ELX, ELY);
609 if (!IN_LEV_FIELD(ELX, ELY) || !IN_PIX_FIELD(LX, LY))
612 laser.dest_element = element;
617 if (hit_mask == (HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT))
619 /* we have hit the top-right and bottom-left element --
620 choose the bottom-left one */
621 /* !!! THIS CAN BE DONE MORE INTELLIGENTLY, FOR EXAMPLE, IF ONE
622 ELEMENT WAS STEEL AND THE OTHER ONE WAS ICE => ALWAYS CHOOSE
623 THE ICE AND MELT IT AWAY INSTEAD OF OVERLOADING LASER !!! */
624 ELX = (LX - 2) / TILEX;
625 ELY = (LY + 2) / TILEY;
628 if (hit_mask == (HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT))
630 /* we have hit the top-left and bottom-right element --
631 choose the top-left one */
632 /* !!! SEE ABOVE !!! */
633 ELX = (LX - 2) / TILEX;
634 ELY = (LY - 2) / TILEY;
638 printf("hit_mask (2) == '%x' (%d, %d) (%d, %d)\n",
639 hit_mask, LX, LY, ELX, ELY);
642 element = Feld[ELX][ELY];
643 laser.dest_element = element;
646 printf("Hit element %d at (%d, %d) [%d, %d] [%d, %d] [%d]\n",
649 LX % TILEX, LY % TILEY,
654 if (!IN_LEV_FIELD(ELX, ELY))
655 printf("WARNING! (1) %d, %d (%d)\n", ELX, ELY, element);
658 if (element == EL_EMPTY)
660 if (!HitOnlyAnEdge(element, hit_mask))
663 else if (element == EL_FUSE_ON)
665 if (HitPolarizer(element, hit_mask))
668 else if (IS_GRID(element) || IS_DF_GRID(element))
670 if (HitPolarizer(element, hit_mask))
673 else if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD ||
674 element == EL_GATE_STONE || element == EL_GATE_WOOD)
676 if (HitBlock(element, hit_mask))
683 else if (IS_MCDUFFIN(element))
685 if (HitLaserSource(element, hit_mask))
688 else if ((element >= EL_EXIT_CLOSED && element <= EL_EXIT_OPEN) ||
689 IS_RECEIVER(element))
691 if (HitLaserDestination(element, hit_mask))
694 else if (IS_WALL(element))
696 if (IS_WALL_STEEL(element) || IS_DF_WALL_STEEL(element))
698 if (HitReflectingWalls(element, hit_mask))
703 if (HitAbsorbingWalls(element, hit_mask))
709 if (HitElement(element, hit_mask))
714 DrawLaser(rf - 1, DL_LASER_ENABLED);
715 rf = laser.num_edges;
719 if (laser.dest_element != Feld[ELX][ELY])
721 printf("ALARM: laser.dest_element == %d, Feld[ELX][ELY] == %d\n",
722 laser.dest_element, Feld[ELX][ELY]);
726 if (!end && !laser.stops_inside_element && !StepBehind())
729 printf("ScanLaser: Go one step back\n");
735 AddLaserEdge(LX, LY);
739 DrawLaser(rf - 1, DL_LASER_ENABLED);
744 if (!IN_LEV_FIELD(ELX, ELY))
745 printf("WARNING! (2) %d, %d\n", ELX, ELY);
749 void DrawLaserExt(int start_edge, int num_edges, int mode)
755 printf("DrawLaserExt: start_edge, num_edges, mode == %d, %d, %d\n",
756 start_edge, num_edges, mode);
761 Error(ERR_WARN, "DrawLaserExt: start_edge < 0");
768 Error(ERR_WARN, "DrawLaserExt: num_edges < 0");
774 if (mode == DL_LASER_DISABLED)
776 printf("DrawLaser: Delete laser from edge %d\n", start_edge);
780 /* now draw the laser to the backbuffer and (if enabled) to the screen */
781 DrawLines(drawto, &laser.edge[start_edge], num_edges,
782 (mode == DL_LASER_ENABLED ? pen_ray : pen_bg));
784 redraw_mask |= REDRAW_FIELD;
786 if (mode == DL_LASER_ENABLED)
789 /* after the laser was deleted, the "damaged" graphics must be restored */
790 if (laser.num_damages)
792 int damage_start = 0;
795 /* determine the starting edge, from which graphics need to be restored */
798 for (i = 0; i < laser.num_damages; i++)
800 if (laser.damage[i].edge == start_edge + 1)
809 /* restore graphics from this starting edge to the end of damage list */
810 for (i = damage_start; i < laser.num_damages; i++)
812 int lx = laser.damage[i].x;
813 int ly = laser.damage[i].y;
814 int element = Feld[lx][ly];
816 if (Hit[lx][ly] == laser.damage[i].edge)
817 if (!((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
820 if (Box[lx][ly] == laser.damage[i].edge)
823 if (IS_DRAWABLE(element))
824 DrawField_MM(lx, ly);
827 elx = laser.damage[damage_start].x;
828 ely = laser.damage[damage_start].y;
829 element = Feld[elx][ely];
832 if (IS_BEAMER(element))
836 for (i = 0; i < laser.num_beamers; i++)
837 printf("-> %d\n", laser.beamer_edge[i]);
838 printf("DrawLaserExt: IS_BEAMER: [%d]: Hit[%d][%d] == %d [%d]\n",
839 mode, elx, ely, Hit[elx][ely], start_edge);
840 printf("DrawLaserExt: IS_BEAMER: %d / %d\n",
841 get_element_angle(element), laser.damage[damage_start].angle);
845 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
846 laser.num_beamers > 0 &&
847 start_edge == laser.beamer_edge[laser.num_beamers - 1])
849 /* element is outgoing beamer */
850 laser.num_damages = damage_start + 1;
852 if (IS_BEAMER(element))
853 laser.current_angle = get_element_angle(element);
857 /* element is incoming beamer or other element */
858 laser.num_damages = damage_start;
859 laser.current_angle = laser.damage[laser.num_damages].angle;
864 /* no damages but McDuffin himself (who needs to be redrawn anyway) */
866 elx = laser.start_edge.x;
867 ely = laser.start_edge.y;
868 element = Feld[elx][ely];
871 laser.num_edges = start_edge + 1;
873 laser.current_angle = laser.start_angle;
875 LX = laser.edge[start_edge].x - (SX + 2);
876 LY = laser.edge[start_edge].y - (SY + 2);
877 XS = 2 * Step[laser.current_angle].x;
878 YS = 2 * Step[laser.current_angle].y;
881 printf("DrawLaser: Set (LX, LY) to (%d, %d) [%d]\n",
887 if (IS_BEAMER(element) ||
888 IS_FIBRE_OPTIC(element) ||
889 IS_PACMAN(element) ||
891 IS_POLAR_CROSS(element) ||
892 element == EL_FUSE_ON)
897 printf("element == %d\n", element);
900 if (IS_22_5_ANGLE(laser.current_angle)) /* neither 90° nor 45° angle */
901 step_size = ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) ? 4 : 3);
905 if (IS_POLAR(element) || IS_POLAR_CROSS(element) ||
906 ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
907 (laser.num_beamers == 0 ||
908 start_edge != laser.beamer_edge[laser.num_beamers - 1])))
910 /* element is incoming beamer or other element */
911 step_size = -step_size;
916 if (IS_BEAMER(element))
918 printf("start_edge == %d, laser.beamer_edge == %d\n",
919 start_edge, laser.beamer_edge);
923 LX += step_size * XS;
924 LY += step_size * YS;
926 else if (element != EL_EMPTY)
935 printf("DrawLaser: Finally: (LX, LY) to (%d, %d) [%d]\n",
940 void DrawLaser(int start_edge, int mode)
942 if (laser.num_edges - start_edge < 0)
944 Error(ERR_WARN, "DrawLaser: laser.num_edges - start_edge < 0");
949 /* check if laser is interrupted by beamer element */
950 if (laser.num_beamers > 0 &&
951 start_edge < laser.beamer_edge[laser.num_beamers - 1])
953 if (mode == DL_LASER_ENABLED)
956 int tmp_start_edge = start_edge;
958 /* draw laser segments forward from the start to the last beamer */
959 for (i = 0; i < laser.num_beamers; i++)
961 int tmp_num_edges = laser.beamer_edge[i] - tmp_start_edge;
963 if (tmp_num_edges <= 0)
967 printf("DrawLaser: DL_LASER_ENABLED: i==%d: %d, %d\n",
968 i, laser.beamer_edge[i], tmp_start_edge);
971 DrawLaserExt(tmp_start_edge, tmp_num_edges, DL_LASER_ENABLED);
973 tmp_start_edge = laser.beamer_edge[i];
976 /* draw last segment from last beamer to the end */
977 DrawLaserExt(tmp_start_edge, laser.num_edges - tmp_start_edge,
983 int last_num_edges = laser.num_edges;
984 int num_beamers = laser.num_beamers;
986 /* delete laser segments backward from the end to the first beamer */
987 for (i = num_beamers-1; i >= 0; i--)
989 int tmp_num_edges = last_num_edges - laser.beamer_edge[i];
991 if (laser.beamer_edge[i] - start_edge <= 0)
994 DrawLaserExt(laser.beamer_edge[i], tmp_num_edges, DL_LASER_DISABLED);
996 last_num_edges = laser.beamer_edge[i];
1001 if (last_num_edges - start_edge <= 0)
1002 printf("DrawLaser: DL_LASER_DISABLED: %d, %d\n",
1003 last_num_edges, start_edge);
1006 /* delete first segment from start to the first beamer */
1007 DrawLaserExt(start_edge, last_num_edges - start_edge, DL_LASER_DISABLED);
1012 DrawLaserExt(start_edge, laser.num_edges - start_edge, mode);
1016 boolean HitElement(int element, int hit_mask)
1018 if (HitOnlyAnEdge(element, hit_mask))
1021 if (IS_MOVING(ELX, ELY) || IS_BLOCKED(ELX, ELY))
1022 element = MovingOrBlocked2Element_MM(ELX, ELY);
1025 printf("HitElement (1): element == %d\n", element);
1029 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1030 printf("HitElement (%d): EXACT MATCH @ (%d, %d)\n", element, ELX, ELY);
1032 printf("HitElement (%d): FUZZY MATCH @ (%d, %d)\n", element, ELX, ELY);
1035 AddDamagedField(ELX, ELY);
1037 /* this is more precise: check if laser would go through the center */
1038 if ((ELX * TILEX + 14 - LX) * YS != (ELY * TILEY + 14 - LY) * XS)
1040 /* skip the whole element before continuing the scan */
1046 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1048 if (LX/TILEX > ELX || LY/TILEY > ELY)
1050 /* skipping scan positions to the right and down skips one scan
1051 position too much, because this is only the top left scan position
1052 of totally four scan positions (plus one to the right, one to the
1053 bottom and one to the bottom right) */
1063 printf("HitElement (2): element == %d\n", element);
1066 if (LX + 5 * XS < 0 ||
1076 printf("HitElement (3): element == %d\n", element);
1079 if (IS_POLAR(element) &&
1080 ((element - EL_POLAR_START) % 2 ||
1081 (element - EL_POLAR_START) / 2 != laser.current_angle % 8))
1083 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1085 laser.num_damages--;
1090 if (IS_POLAR_CROSS(element) &&
1091 (element - EL_POLAR_CROSS_START) != laser.current_angle % 4)
1093 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1095 laser.num_damages--;
1100 if (!IS_BEAMER(element) &&
1101 !IS_FIBRE_OPTIC(element) &&
1102 !IS_GRID_WOOD(element) &&
1103 element != EL_FUEL_EMPTY)
1106 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1107 printf("EXACT MATCH @ (%d, %d)\n", ELX, ELY);
1109 printf("FUZZY MATCH @ (%d, %d)\n", ELX, ELY);
1112 LX = ELX * TILEX + 14;
1113 LY = ELY * TILEY + 14;
1115 AddLaserEdge(LX, LY);
1118 if (IS_MIRROR(element) ||
1119 IS_MIRROR_FIXED(element) ||
1120 IS_POLAR(element) ||
1121 IS_POLAR_CROSS(element) ||
1122 IS_DF_MIRROR(element) ||
1123 IS_DF_MIRROR_AUTO(element) ||
1124 element == EL_PRISM ||
1125 element == EL_REFRACTOR)
1127 int current_angle = laser.current_angle;
1130 laser.num_damages--;
1132 AddDamagedField(ELX, ELY);
1134 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1137 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1139 if (IS_MIRROR(element) ||
1140 IS_MIRROR_FIXED(element) ||
1141 IS_DF_MIRROR(element) ||
1142 IS_DF_MIRROR_AUTO(element))
1143 laser.current_angle = get_mirrored_angle(laser.current_angle,
1144 get_element_angle(element));
1146 if (element == EL_PRISM || element == EL_REFRACTOR)
1147 laser.current_angle = RND(16);
1149 XS = 2 * Step[laser.current_angle].x;
1150 YS = 2 * Step[laser.current_angle].y;
1152 if (!IS_22_5_ANGLE(laser.current_angle)) /* 90° or 45° angle */
1157 LX += step_size * XS;
1158 LY += step_size * YS;
1161 /* draw sparkles on mirror */
1162 if ((IS_MIRROR(element) || IS_MIRROR_FIXED(element)) &&
1163 current_angle != laser.current_angle)
1165 MoveSprite(vp, &Pfeil[2], 4 + 16 * ELX, 5 + 16 * ELY + 1);
1169 if ((!IS_POLAR(element) && !IS_POLAR_CROSS(element)) &&
1170 current_angle != laser.current_angle)
1171 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1174 (get_opposite_angle(laser.current_angle) ==
1175 laser.damage[laser.num_damages - 1].angle ? TRUE : FALSE);
1177 return (laser.overloaded ? TRUE : FALSE);
1180 if (element == EL_FUEL_FULL)
1182 laser.stops_inside_element = TRUE;
1187 if (element == EL_BOMB || element == EL_MINE)
1189 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1191 if (element == EL_MINE)
1192 laser.overloaded = TRUE;
1195 if (element == EL_KETTLE ||
1196 element == EL_CELL ||
1197 element == EL_KEY ||
1198 element == EL_LIGHTBALL ||
1199 element == EL_PACMAN ||
1202 if (!IS_PACMAN(element))
1205 if (element == EL_PACMAN)
1208 if (element == EL_KETTLE || element == EL_CELL)
1210 if (game_mm.kettles_still_needed > 0)
1211 game_mm.kettles_still_needed--;
1215 if (game_mm.kettles_still_needed == 0)
1217 int exit_element = (element == EL_KETTLE ? EL_EXIT_OPEN : EL_RECEIVER);
1219 static int xy[4][2] =
1227 PlayLevelSound_MM(ELX, ELY, exit_element, MM_ACTION_OPENING);
1229 for (y = 0; y < lev_fieldy; y++)
1231 for (x = 0; x < lev_fieldx; x++)
1233 /* initiate opening animation of exit door */
1234 if (Feld[x][y] == EL_EXIT_CLOSED)
1235 Feld[x][y] = EL_EXIT_OPENING;
1237 /* remove field that blocks receiver */
1238 if (IS_RECEIVER(Feld[x][y]))
1240 int phase = Feld[x][y] - EL_RECEIVER_START;
1241 int blocking_x, blocking_y;
1243 blocking_x = x + xy[phase][0];
1244 blocking_y = y + xy[phase][1];
1246 if (IN_LEV_FIELD(blocking_x, blocking_y))
1248 Feld[blocking_x][blocking_y] = EL_EMPTY;
1250 DrawField_MM(blocking_x, blocking_y);
1256 DrawLaser(0, DL_LASER_ENABLED);
1259 else if (element == EL_KEY)
1263 else if (element == EL_LIGHTBALL)
1267 else if (IS_PACMAN(element))
1269 DeletePacMan(ELX, ELY);
1276 if (element == EL_LIGHTBULB_OFF || element == EL_LIGHTBULB_ON)
1278 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1280 DrawLaser(0, DL_LASER_ENABLED);
1282 if (Feld[ELX][ELY] == EL_LIGHTBULB_OFF)
1284 Feld[ELX][ELY] = EL_LIGHTBULB_ON;
1285 game_mm.lights_still_needed--;
1289 Feld[ELX][ELY] = EL_LIGHTBULB_OFF;
1290 game_mm.lights_still_needed++;
1293 DrawField_MM(ELX, ELY);
1294 DrawLaser(0, DL_LASER_ENABLED);
1299 laser.stops_inside_element = TRUE;
1305 printf("HitElement (4): element == %d\n", element);
1308 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1309 laser.num_beamers < MAX_NUM_BEAMERS &&
1310 laser.beamer[BEAMER_NR(element)][1].num)
1312 int beamer_angle = get_element_angle(element);
1313 int beamer_nr = BEAMER_NR(element);
1317 printf("HitElement (BEAMER): element == %d\n", element);
1320 laser.num_damages--;
1322 if (IS_FIBRE_OPTIC(element) ||
1323 laser.current_angle == get_opposite_angle(beamer_angle))
1327 LX = ELX * TILEX + 14;
1328 LY = ELY * TILEY + 14;
1330 AddLaserEdge(LX, LY);
1331 AddDamagedField(ELX, ELY);
1333 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1336 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1338 pos = (ELX == laser.beamer[beamer_nr][0].x &&
1339 ELY == laser.beamer[beamer_nr][0].y ? 1 : 0);
1340 ELX = laser.beamer[beamer_nr][pos].x;
1341 ELY = laser.beamer[beamer_nr][pos].y;
1342 LX = ELX * TILEX + 14;
1343 LY = ELY * TILEY + 14;
1345 if (IS_BEAMER(element))
1347 laser.current_angle = get_element_angle(Feld[ELX][ELY]);
1348 XS = 2 * Step[laser.current_angle].x;
1349 YS = 2 * Step[laser.current_angle].y;
1352 laser.beamer_edge[laser.num_beamers] = laser.num_edges;
1354 AddLaserEdge(LX, LY);
1355 AddDamagedField(ELX, ELY);
1357 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1360 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1362 if (laser.current_angle == (laser.current_angle >> 1) << 1)
1367 LX += step_size * XS;
1368 LY += step_size * YS;
1370 laser.num_beamers++;
1379 boolean HitOnlyAnEdge(int element, int hit_mask)
1381 /* check if the laser hit only the edge of an element and, if so, go on */
1384 printf("LX, LY, hit_mask == %d, %d, %d\n", LX, LY, hit_mask);
1387 if ((hit_mask == HIT_MASK_TOPLEFT ||
1388 hit_mask == HIT_MASK_TOPRIGHT ||
1389 hit_mask == HIT_MASK_BOTTOMLEFT ||
1390 hit_mask == HIT_MASK_BOTTOMRIGHT) &&
1391 laser.current_angle % 4) /* angle is not 90° */
1395 if (hit_mask == HIT_MASK_TOPLEFT)
1400 else if (hit_mask == HIT_MASK_TOPRIGHT)
1405 else if (hit_mask == HIT_MASK_BOTTOMLEFT)
1410 else /* (hit_mask == HIT_MASK_BOTTOMRIGHT) */
1416 AddDamagedField((LX + 2 * dx) / TILEX, (LY + 2 * dy) / TILEY);
1422 printf("[HitOnlyAnEdge() == TRUE]\n");
1429 printf("[HitOnlyAnEdge() == FALSE]\n");
1435 boolean HitPolarizer(int element, int hit_mask)
1437 if (HitOnlyAnEdge(element, hit_mask))
1440 if (IS_DF_GRID(element))
1442 int grid_angle = get_element_angle(element);
1445 printf("HitPolarizer: angle: grid == %d, laser == %d\n",
1446 grid_angle, laser.current_angle);
1449 AddLaserEdge(LX, LY);
1450 AddDamagedField(ELX, ELY);
1453 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1455 if (laser.current_angle == grid_angle ||
1456 laser.current_angle == get_opposite_angle(grid_angle))
1458 /* skip the whole element before continuing the scan */
1464 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1466 if (LX/TILEX > ELX || LY/TILEY > ELY)
1468 /* skipping scan positions to the right and down skips one scan
1469 position too much, because this is only the top left scan position
1470 of totally four scan positions (plus one to the right, one to the
1471 bottom and one to the bottom right) */
1477 AddLaserEdge(LX, LY);
1483 printf("HitPolarizer: LX, LY == %d, %d [%d, %d] [%d, %d]\n",
1485 LX / TILEX, LY / TILEY,
1486 LX % TILEX, LY % TILEY);
1491 else if (IS_GRID_STEEL_FIXED(element) || IS_GRID_STEEL_AUTO(element))
1493 return HitReflectingWalls(element, hit_mask);
1497 return HitAbsorbingWalls(element, hit_mask);
1500 else if (IS_GRID_STEEL(element))
1502 return HitReflectingWalls(element, hit_mask);
1504 else /* IS_GRID_WOOD */
1506 return HitAbsorbingWalls(element, hit_mask);
1512 boolean HitBlock(int element, int hit_mask)
1514 boolean check = FALSE;
1516 if ((element == EL_GATE_STONE || element == EL_GATE_WOOD) &&
1517 game_mm.num_keys == 0)
1520 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1523 int ex = ELX * TILEX + 14;
1524 int ey = ELY * TILEY + 14;
1528 for (i = 1; i < 32; i++)
1533 if ((x == ex || x == ex + 1) && (y == ey || y == ey + 1))
1538 if (check && (element == EL_BLOCK_WOOD || element == EL_GATE_WOOD))
1539 return HitAbsorbingWalls(element, hit_mask);
1543 AddLaserEdge(LX - XS, LY - YS);
1544 AddDamagedField(ELX, ELY);
1547 Box[ELX][ELY] = laser.num_edges;
1549 return HitReflectingWalls(element, hit_mask);
1552 if (element == EL_GATE_STONE || element == EL_GATE_WOOD)
1554 int xs = XS / 2, ys = YS / 2;
1555 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1556 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1558 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1559 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1561 laser.overloaded = (element == EL_GATE_STONE);
1566 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1567 (hit_mask == HIT_MASK_TOP ||
1568 hit_mask == HIT_MASK_LEFT ||
1569 hit_mask == HIT_MASK_RIGHT ||
1570 hit_mask == HIT_MASK_BOTTOM))
1571 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1572 hit_mask == HIT_MASK_BOTTOM),
1573 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1574 hit_mask == HIT_MASK_RIGHT));
1575 AddLaserEdge(LX, LY);
1581 if (element == EL_GATE_STONE && Box[ELX][ELY])
1583 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
1595 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1597 int xs = XS / 2, ys = YS / 2;
1598 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1599 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1601 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1602 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1604 laser.overloaded = (element == EL_BLOCK_STONE);
1609 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1610 (hit_mask == HIT_MASK_TOP ||
1611 hit_mask == HIT_MASK_LEFT ||
1612 hit_mask == HIT_MASK_RIGHT ||
1613 hit_mask == HIT_MASK_BOTTOM))
1614 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1615 hit_mask == HIT_MASK_BOTTOM),
1616 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1617 hit_mask == HIT_MASK_RIGHT));
1618 AddDamagedField(ELX, ELY);
1620 LX = ELX * TILEX + 14;
1621 LY = ELY * TILEY + 14;
1623 AddLaserEdge(LX, LY);
1625 laser.stops_inside_element = TRUE;
1633 boolean HitLaserSource(int element, int hit_mask)
1635 if (HitOnlyAnEdge(element, hit_mask))
1638 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1640 laser.overloaded = TRUE;
1645 boolean HitLaserDestination(int element, int hit_mask)
1647 if (HitOnlyAnEdge(element, hit_mask))
1650 if (element != EL_EXIT_OPEN &&
1651 !(IS_RECEIVER(element) &&
1652 game_mm.kettles_still_needed == 0 &&
1653 laser.current_angle == get_opposite_angle(get_element_angle(element))))
1655 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1660 if (IS_RECEIVER(element) ||
1661 (IS_22_5_ANGLE(laser.current_angle) &&
1662 (ELX != (LX + 6 * XS) / TILEX ||
1663 ELY != (LY + 6 * YS) / TILEY ||
1672 LX = ELX * TILEX + 14;
1673 LY = ELY * TILEY + 14;
1675 laser.stops_inside_element = TRUE;
1678 AddLaserEdge(LX, LY);
1679 AddDamagedField(ELX, ELY);
1681 if (game_mm.lights_still_needed == 0)
1682 game_mm.level_solved = TRUE;
1687 boolean HitReflectingWalls(int element, int hit_mask)
1689 /* check if laser hits side of a wall with an angle that is not 90° */
1690 if (!IS_90_ANGLE(laser.current_angle) && (hit_mask == HIT_MASK_TOP ||
1691 hit_mask == HIT_MASK_LEFT ||
1692 hit_mask == HIT_MASK_RIGHT ||
1693 hit_mask == HIT_MASK_BOTTOM))
1695 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1700 if (!IS_DF_GRID(element))
1701 AddLaserEdge(LX, LY);
1703 /* check if laser hits wall with an angle of 45° */
1704 if (!IS_22_5_ANGLE(laser.current_angle))
1706 if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1709 laser.current_angle = get_mirrored_angle(laser.current_angle,
1712 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1715 laser.current_angle = get_mirrored_angle(laser.current_angle,
1719 AddLaserEdge(LX, LY);
1721 XS = 2 * Step[laser.current_angle].x;
1722 YS = 2 * Step[laser.current_angle].y;
1726 else if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1728 laser.current_angle = get_mirrored_angle(laser.current_angle,
1733 if (!IS_DF_GRID(element))
1734 AddLaserEdge(LX, LY);
1739 if (!IS_DF_GRID(element))
1740 AddLaserEdge(LX, LY + YS / 2);
1743 if (!IS_DF_GRID(element))
1744 AddLaserEdge(LX, LY);
1747 YS = 2 * Step[laser.current_angle].y;
1751 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1753 laser.current_angle = get_mirrored_angle(laser.current_angle,
1758 if (!IS_DF_GRID(element))
1759 AddLaserEdge(LX, LY);
1764 if (!IS_DF_GRID(element))
1765 AddLaserEdge(LX + XS / 2, LY);
1768 if (!IS_DF_GRID(element))
1769 AddLaserEdge(LX, LY);
1772 XS = 2 * Step[laser.current_angle].x;
1778 /* reflection at the edge of reflecting DF style wall */
1779 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1781 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1782 hit_mask == HIT_MASK_TOPRIGHT) ||
1783 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1784 hit_mask == HIT_MASK_TOPLEFT) ||
1785 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1786 hit_mask == HIT_MASK_BOTTOMLEFT) ||
1787 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1788 hit_mask == HIT_MASK_BOTTOMRIGHT))
1791 (hit_mask == HIT_MASK_TOPRIGHT || hit_mask == HIT_MASK_BOTTOMLEFT ?
1792 ANG_MIRROR_135 : ANG_MIRROR_45);
1794 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1796 AddDamagedField(ELX, ELY);
1797 AddLaserEdge(LX, LY);
1799 laser.current_angle = get_mirrored_angle(laser.current_angle,
1807 AddLaserEdge(LX, LY);
1813 /* reflection inside an edge of reflecting DF style wall */
1814 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1816 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1817 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT)) ||
1818 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1819 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMRIGHT)) ||
1820 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1821 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT)) ||
1822 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1823 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPLEFT)))
1826 (hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT) ||
1827 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT) ?
1828 ANG_MIRROR_135 : ANG_MIRROR_45);
1830 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1833 AddDamagedField(ELX, ELY);
1836 AddLaserEdge(LX - XS, LY - YS);
1837 AddLaserEdge(LX - XS + (ABS(XS) == 4 ? XS/2 : 0),
1838 LY - YS + (ABS(YS) == 4 ? YS/2 : 0));
1840 laser.current_angle = get_mirrored_angle(laser.current_angle,
1848 AddLaserEdge(LX, LY);
1854 /* check if laser hits DF style wall with an angle of 90° */
1855 if (IS_DF_WALL(element) && IS_90_ANGLE(laser.current_angle))
1857 if ((IS_HORIZ_ANGLE(laser.current_angle) &&
1858 (!(hit_mask & HIT_MASK_TOP) || !(hit_mask & HIT_MASK_BOTTOM))) ||
1859 (IS_VERT_ANGLE(laser.current_angle) &&
1860 (!(hit_mask & HIT_MASK_LEFT) || !(hit_mask & HIT_MASK_RIGHT))))
1862 static int last_LX = 0, last_LY = 0, last_hit_mask = 0;
1864 /* laser at last step touched nothing or the same side of the wall */
1865 if (LX != last_LX || LY != last_LY || hit_mask == last_hit_mask)
1867 AddDamagedField(ELX, ELY);
1874 last_hit_mask = hit_mask;
1881 if (!HitOnlyAnEdge(element, hit_mask))
1883 laser.overloaded = TRUE;
1891 boolean HitAbsorbingWalls(int element, int hit_mask)
1893 if (HitOnlyAnEdge(element, hit_mask))
1897 (hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT))
1899 AddLaserEdge(LX - XS, LY - YS);
1906 (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM))
1908 AddLaserEdge(LX - XS, LY - YS);
1914 if (IS_WALL_WOOD(element) ||
1915 IS_DF_WALL_WOOD(element) ||
1916 IS_GRID_WOOD(element) ||
1917 IS_GRID_WOOD_FIXED(element) ||
1918 IS_GRID_WOOD_AUTO(element) ||
1919 element == EL_FUSE_ON ||
1920 element == EL_BLOCK_WOOD ||
1921 element == EL_GATE_WOOD)
1923 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1928 if (IS_WALL_ICE(element))
1932 mask = (LX + XS) / MINI_TILEX - ELX * 2 + 1; /* Quadrant (horizontal) */
1933 mask <<= (((LY + YS) / MINI_TILEY - ELY * 2) > 0) * 2; /* || (vertical) */
1935 /* check if laser hits wall with an angle of 90° */
1936 if (IS_90_ANGLE(laser.current_angle))
1937 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1939 if (mask == 1 || mask == 2 || mask == 4 || mask == 8)
1943 for (i = 0; i < 4; i++)
1945 if (mask == (1 << i) && (XS > 0) == (i % 2) && (YS > 0) == (i / 2))
1946 mask = 15 - (8 >> i);
1947 else if (ABS(XS) == 4 &&
1949 (XS > 0) == (i % 2) &&
1950 (YS < 0) == (i / 2))
1951 mask = 3 + (i / 2) * 9;
1952 else if (ABS(YS) == 4 &&
1954 (XS < 0) == (i % 2) &&
1955 (YS > 0) == (i / 2))
1956 mask = 5 + (i % 2) * 5;
1960 laser.wall_mask = mask;
1962 else if (IS_WALL_AMOEBA(element))
1964 int elx = (LX - 2 * XS) / TILEX;
1965 int ely = (LY - 2 * YS) / TILEY;
1966 int element2 = Feld[elx][ely];
1969 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element2))
1971 laser.dest_element = EL_EMPTY;
1979 mask = (LX - 2 * XS) / 16 - ELX * 2 + 1;
1980 mask <<= ((LY - 2 * YS) / 16 - ELY * 2) * 2;
1982 if (IS_90_ANGLE(laser.current_angle))
1983 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1985 laser.dest_element = element2 | EL_WALL_AMOEBA;
1987 laser.wall_mask = mask;
1993 void OpenExit(int x, int y)
1997 if (!MovDelay[x][y]) /* next animation frame */
1998 MovDelay[x][y] = 4 * delay;
2000 if (MovDelay[x][y]) /* wait some time before next frame */
2005 phase = MovDelay[x][y] / delay;
2007 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2008 DrawGraphicAnimation_MM(x, y, IMG_MM_EXIT_OPENING, 3 - phase);
2010 if (!MovDelay[x][y])
2012 Feld[x][y] = EL_EXIT_OPEN;
2018 void OpenSurpriseBall(int x, int y)
2022 if (!MovDelay[x][y]) /* next animation frame */
2023 MovDelay[x][y] = 50 * delay;
2025 if (MovDelay[x][y]) /* wait some time before next frame */
2029 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2032 int graphic = el2gfx(Store[x][y]);
2034 int dx = RND(26), dy = RND(26);
2036 getGraphicSource(graphic, 0, &bitmap, &gx, &gy);
2038 BlitBitmap(bitmap, drawto, gx + dx, gy + dy, 6, 6,
2039 SX + x * TILEX + dx, SY + y * TILEY + dy);
2041 MarkTileDirty(x, y);
2044 if (!MovDelay[x][y])
2046 Feld[x][y] = Store[x][y];
2055 void MeltIce(int x, int y)
2060 if (!MovDelay[x][y]) /* next animation frame */
2061 MovDelay[x][y] = frames * delay;
2063 if (MovDelay[x][y]) /* wait some time before next frame */
2066 int wall_mask = Store2[x][y];
2067 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_ICE;
2070 phase = frames - MovDelay[x][y] / delay - 1;
2072 if (!MovDelay[x][y])
2076 Feld[x][y] = real_element & (wall_mask ^ 0xFF);
2077 Store[x][y] = Store2[x][y] = 0;
2079 DrawWalls_MM(x, y, Feld[x][y]);
2081 if (Feld[x][y] == EL_WALL_ICE)
2082 Feld[x][y] = EL_EMPTY;
2084 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
2085 if (laser.damage[i].is_mirror)
2089 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
2091 DrawLaser(0, DL_LASER_DISABLED);
2095 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2097 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2099 laser.redraw = TRUE;
2104 void GrowAmoeba(int x, int y)
2109 if (!MovDelay[x][y]) /* next animation frame */
2110 MovDelay[x][y] = frames * delay;
2112 if (MovDelay[x][y]) /* wait some time before next frame */
2115 int wall_mask = Store2[x][y];
2116 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_AMOEBA;
2119 phase = MovDelay[x][y] / delay;
2121 if (!MovDelay[x][y])
2123 Feld[x][y] = real_element;
2124 Store[x][y] = Store2[x][y] = 0;
2126 DrawWalls_MM(x, y, Feld[x][y]);
2127 DrawLaser(0, DL_LASER_ENABLED);
2129 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2131 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2136 static void Explode_MM(int x, int y, int phase, int mode)
2138 int num_phase = 9, delay = 2;
2139 int last_phase = num_phase * delay;
2140 int half_phase = (num_phase / 2) * delay;
2142 laser.redraw = TRUE;
2144 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2146 int center_element = Feld[x][y];
2148 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2150 /* put moving element to center field (and let it explode there) */
2151 center_element = MovingOrBlocked2Element_MM(x, y);
2152 RemoveMovingField_MM(x, y);
2154 Feld[x][y] = center_element;
2157 if (center_element == EL_BOMB || IS_MCDUFFIN(center_element))
2158 Store[x][y] = center_element;
2160 Store[x][y] = EL_EMPTY;
2162 Store2[x][y] = mode;
2163 Feld[x][y] = EL_EXPLODING_OPAQUE;
2164 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2170 Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
2172 if (phase == half_phase)
2174 Feld[x][y] = EL_EXPLODING_TRANSP;
2176 if (x == ELX && y == ELY)
2180 if (phase == last_phase)
2182 if (Store[x][y] == EL_BOMB)
2184 laser.num_damages--;
2185 DrawLaser(0, DL_LASER_DISABLED);
2186 laser.num_edges = 0;
2188 Bang_MM(laser.start_edge.x, laser.start_edge.y);
2189 Store[x][y] = EL_EMPTY;
2191 else if (IS_MCDUFFIN(Store[x][y]))
2193 game_mm.game_over = TRUE;
2194 game_mm.game_over_cause = GAME_OVER_BOMB;
2195 Store[x][y] = EL_EMPTY;
2198 Feld[x][y] = Store[x][y];
2199 Store[x][y] = Store2[x][y] = 0;
2200 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2202 InitField(x, y, FALSE);
2205 else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2207 int graphic = IMG_MM_DEFAULT_EXPLODING;
2208 int graphic_phase = (phase / delay - 1);
2212 if (Store2[x][y] == EX_KETTLE)
2214 if (graphic_phase < 3)
2216 graphic = IMG_MM_KETTLE_EXPLODING;
2218 else if (graphic_phase < 5)
2224 graphic = IMG_EMPTY;
2228 else if (Store2[x][y] == EX_SHORT)
2230 if (graphic_phase < 4)
2236 graphic = IMG_EMPTY;
2241 getGraphicSource(graphic, graphic_phase, &bitmap, &src_x, &src_y);
2243 BlitBitmap(bitmap, drawto_field, src_x, src_y, TILEX, TILEY,
2244 FX + x * TILEX, FY + y * TILEY);
2246 MarkTileDirty(x, y);
2250 static void Bang_MM(int x, int y)
2252 int element = Feld[x][y];
2253 int mode = EX_NORMAL;
2256 DrawLaser(0, DL_LASER_ENABLED);
2275 if (IS_PACMAN(element))
2276 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2277 else if (element == EL_BOMB || IS_MCDUFFIN(element))
2278 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2279 else if (element == EL_KEY)
2280 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2282 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2284 Explode_MM(x, y, EX_PHASE_START, mode);
2287 void TurnRound(int x, int y)
2299 { 0, 0 }, { 0, 0 }, { 0, 0 },
2304 int left, right, back;
2308 { MV_DOWN, MV_UP, MV_RIGHT },
2309 { MV_UP, MV_DOWN, MV_LEFT },
2311 { MV_LEFT, MV_RIGHT, MV_DOWN },
2312 { 0,0,0 }, { 0,0,0 }, { 0,0,0 },
2313 { MV_RIGHT, MV_LEFT, MV_UP }
2316 int element = Feld[x][y];
2317 int old_move_dir = MovDir[x][y];
2318 int right_dir = turn[old_move_dir].right;
2319 int back_dir = turn[old_move_dir].back;
2320 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
2321 int right_x = x + right_dx, right_y = y + right_dy;
2323 if (element == EL_PACMAN)
2325 boolean can_turn_right = FALSE;
2327 if (IN_LEV_FIELD(right_x, right_y) &&
2328 IS_EATABLE4PACMAN(Feld[right_x][right_y]))
2329 can_turn_right = TRUE;
2332 MovDir[x][y] = right_dir;
2334 MovDir[x][y] = back_dir;
2340 static void StartMoving_MM(int x, int y)
2342 int element = Feld[x][y];
2347 if (CAN_MOVE(element))
2351 if (MovDelay[x][y]) /* wait some time before next movement */
2359 /* now make next step */
2361 Moving2Blocked_MM(x, y, &newx, &newy); /* get next screen position */
2363 if (element == EL_PACMAN &&
2364 IN_LEV_FIELD(newx, newy) && IS_EATABLE4PACMAN(Feld[newx][newy]) &&
2365 !ObjHit(newx, newy, HIT_POS_CENTER))
2367 Store[newx][newy] = Feld[newx][newy];
2368 Feld[newx][newy] = EL_EMPTY;
2370 DrawField_MM(newx, newy);
2372 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy) ||
2373 ObjHit(newx, newy, HIT_POS_CENTER))
2375 /* object was running against a wall */
2382 InitMovingField_MM(x, y, MovDir[x][y]);
2386 ContinueMoving_MM(x, y);
2389 static void ContinueMoving_MM(int x, int y)
2391 int element = Feld[x][y];
2392 int direction = MovDir[x][y];
2393 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2394 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2395 int horiz_move = (dx!=0);
2396 int newx = x + dx, newy = y + dy;
2397 int step = (horiz_move ? dx : dy) * TILEX / 8;
2399 MovPos[x][y] += step;
2401 if (ABS(MovPos[x][y]) >= TILEX) /* object reached its destination */
2403 Feld[x][y] = EL_EMPTY;
2404 Feld[newx][newy] = element;
2406 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2407 MovDelay[newx][newy] = 0;
2409 if (!CAN_MOVE(element))
2410 MovDir[newx][newy] = 0;
2413 DrawField_MM(newx, newy);
2415 Stop[newx][newy] = TRUE;
2417 if (element == EL_PACMAN)
2419 if (Store[newx][newy] == EL_BOMB)
2420 Bang_MM(newx, newy);
2422 if (IS_WALL_AMOEBA(Store[newx][newy]) &&
2423 (LX + 2 * XS) / TILEX == newx &&
2424 (LY + 2 * YS) / TILEY == newy)
2431 else /* still moving on */
2436 laser.redraw = TRUE;
2439 void ClickElement(int x, int y, int button)
2441 static unsigned int click_delay = 0;
2442 static int click_delay_value = CLICK_DELAY;
2443 static boolean new_button = TRUE;
2446 /* do not rotate objects hit by the laser after the game was solved */
2447 if (game_mm.level_solved && Hit[x][y])
2450 if (button == MB_RELEASED)
2453 click_delay_value = CLICK_DELAY;
2455 /* release eventually hold auto-rotating mirror */
2456 RotateMirror(x, y, MB_RELEASED);
2461 if (!FrameReached(&click_delay, click_delay_value) && !new_button)
2464 if (button == MB_MIDDLEBUTTON) /* middle button has no function */
2467 if (!IN_LEV_FIELD(x, y))
2470 if (Feld[x][y] == EL_EMPTY)
2473 element = Feld[x][y];
2475 if (IS_MIRROR(element) ||
2476 IS_BEAMER(element) ||
2477 IS_POLAR(element) ||
2478 IS_POLAR_CROSS(element) ||
2479 IS_DF_MIRROR(element) ||
2480 IS_DF_MIRROR_AUTO(element))
2482 RotateMirror(x, y, button);
2484 else if (IS_MCDUFFIN(element))
2486 if (!laser.fuse_off)
2488 DrawLaser(0, DL_LASER_DISABLED);
2495 element = get_rotated_element(element, BUTTON_ROTATION(button));
2496 laser.start_angle = get_element_angle(element);
2500 Feld[x][y] = element;
2507 if (!laser.fuse_off)
2510 else if (element == EL_FUSE_ON && laser.fuse_off)
2512 if (x != laser.fuse_x || y != laser.fuse_y)
2515 laser.fuse_off = FALSE;
2516 laser.fuse_x = laser.fuse_y = -1;
2518 DrawGraphic_MM(x, y, IMG_MM_FUSE_ACTIVE);
2521 else if (element == EL_FUSE_ON && !laser.fuse_off && new_button)
2523 laser.fuse_off = TRUE;
2526 laser.overloaded = FALSE;
2528 DrawLaser(0, DL_LASER_DISABLED);
2529 DrawGraphic_MM(x, y, IMG_MM_FUSE);
2531 else if (element == EL_LIGHTBALL)
2535 DrawLaser(0, DL_LASER_ENABLED);
2538 click_delay_value = (new_button ? CLICK_DELAY_FIRST : CLICK_DELAY);
2542 void RotateMirror(int x, int y, int button)
2544 static int hold_x = -1, hold_y = -1;
2546 if (button == MB_RELEASED)
2548 /* release eventually hold auto-rotating mirror */
2555 if (IS_MIRROR(Feld[x][y]) ||
2556 IS_POLAR_CROSS(Feld[x][y]) ||
2557 IS_POLAR(Feld[x][y]) ||
2558 IS_BEAMER(Feld[x][y]) ||
2559 IS_DF_MIRROR(Feld[x][y]) ||
2560 IS_GRID_STEEL_AUTO(Feld[x][y]) ||
2561 IS_GRID_WOOD_AUTO(Feld[x][y]))
2563 Feld[x][y] = get_rotated_element(Feld[x][y], BUTTON_ROTATION(button));
2565 else if (IS_DF_MIRROR_AUTO(Feld[x][y]))
2567 if (button == MB_LEFTBUTTON)
2569 /* left mouse button only for manual adjustment, no auto-rotating;
2570 freeze mirror for until mouse button released */
2574 else if (button == MB_RIGHTBUTTON && (hold_x != x || hold_y != y))
2576 Feld[x][y] = get_rotated_element(Feld[x][y], ROTATE_RIGHT);
2580 if (IS_GRID_STEEL_AUTO(Feld[x][y]) || IS_GRID_WOOD_AUTO(Feld[x][y]))
2582 int edge = Hit[x][y];
2588 DrawLaser(edge - 1, DL_LASER_DISABLED);
2592 else if (ObjHit(x, y, HIT_POS_CENTER))
2594 int edge = Hit[x][y];
2598 Error(ERR_WARN, "RotateMirror: inconsistent field Hit[][]!\n");
2602 DrawLaser(edge - 1, DL_LASER_DISABLED);
2609 if (ObjHit(x, y, HIT_POS_EDGE | HIT_POS_BETWEEN))
2614 if ((IS_BEAMER(Feld[x][y]) ||
2615 IS_POLAR(Feld[x][y]) ||
2616 IS_POLAR_CROSS(Feld[x][y])) && x == ELX && y == ELY)
2620 if (IS_BEAMER(Feld[x][y]))
2623 printf("TEST (%d, %d) [%d] [%d]\n",
2625 laser.beamer_edge, laser.beamer[1].num);
2635 DrawLaser(0, DL_LASER_ENABLED);
2639 void AutoRotateMirrors()
2641 static unsigned int rotate_delay = 0;
2644 if (!FrameReached(&rotate_delay, AUTO_ROTATE_DELAY))
2647 for (x = 0; x < lev_fieldx; x++)
2649 for (y = 0; y < lev_fieldy; y++)
2651 int element = Feld[x][y];
2653 /* do not rotate objects hit by the laser after the game was solved */
2654 if (game_mm.level_solved && Hit[x][y])
2657 if (IS_DF_MIRROR_AUTO(element) ||
2658 IS_GRID_WOOD_AUTO(element) ||
2659 IS_GRID_STEEL_AUTO(element) ||
2660 element == EL_REFRACTOR)
2661 RotateMirror(x, y, MB_RIGHTBUTTON);
2666 boolean ObjHit(int obx, int oby, int bits)
2673 if (bits & HIT_POS_CENTER)
2675 if (ReadPixel(drawto, SX + obx + 15, SY + oby + 15) == pen_ray)
2679 if (bits & HIT_POS_EDGE)
2681 for (i = 0; i < 4; i++)
2682 if (ReadPixel(drawto,
2683 SX + obx + 31 * (i % 2),
2684 SY + oby + 31 * (i / 2)) == pen_ray)
2688 if (bits & HIT_POS_BETWEEN)
2690 for (i = 0; i < 4; i++)
2691 if (ReadPixel(drawto,
2692 SX + 4 + obx + 22 * (i % 2),
2693 SY + 4 + oby + 22 * (i / 2)) == pen_ray)
2700 void DeletePacMan(int px, int py)
2706 if (game_mm.num_pacman <= 1)
2708 game_mm.num_pacman = 0;
2712 for (i = 0; i < game_mm.num_pacman; i++)
2713 if (game_mm.pacman[i].x == px && game_mm.pacman[i].y == py)
2716 game_mm.num_pacman--;
2718 for (j = i; j < game_mm.num_pacman; j++)
2720 game_mm.pacman[j].x = game_mm.pacman[j + 1].x;
2721 game_mm.pacman[j].y = game_mm.pacman[j + 1].y;
2722 game_mm.pacman[j].dir = game_mm.pacman[j + 1].dir;
2726 void ColorCycling(void)
2728 static int CC, Cc = 0;
2730 static int color, old = 0xF00, new = 0x010, mult = 1;
2731 static unsigned short red, green, blue;
2733 if (color_status == STATIC_COLORS)
2738 if (CC < Cc || CC > Cc + 50)
2742 color = old + new * mult;
2748 if (ABS(mult) == 16)
2758 red = 0x0e00 * ((color & 0xF00) >> 8);
2759 green = 0x0e00 * ((color & 0x0F0) >> 4);
2760 blue = 0x0e00 * ((color & 0x00F));
2761 SetRGB(pen_magicolor[0], red, green, blue);
2763 red = 0x1111 * ((color & 0xF00) >> 8);
2764 green = 0x1111 * ((color & 0x0F0) >> 4);
2765 blue = 0x1111 * ((color & 0x00F));
2766 SetRGB(pen_magicolor[1], red, green, blue);
2770 static void GameActions_MM_Ext(struct MouseActionInfo action, boolean warp_mode)
2772 static unsigned int pacman_delay = 0;
2773 static unsigned int energy_delay = 0;
2774 static unsigned int overload_delay = 0;
2780 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2783 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2785 element = Feld[x][y];
2787 if (!IS_MOVING(x, y) && CAN_MOVE(element))
2788 StartMoving_MM(x, y);
2789 else if (IS_MOVING(x, y))
2790 ContinueMoving_MM(x, y);
2791 else if (IS_EXPLODING(element))
2792 Explode_MM(x, y, Frame[x][y], EX_NORMAL);
2793 else if (element == EL_EXIT_OPENING)
2795 else if (element == EL_GRAY_BALL_OPENING)
2796 OpenSurpriseBall(x, y);
2797 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_ICE)
2799 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_AMOEBA)
2803 AutoRotateMirrors();
2806 /* !!! CHANGE THIS: REDRAW ONLY WHEN NEEDED !!! */
2808 /* redraw after Explode_MM() ... */
2810 DrawLaser(0, DL_LASER_ENABLED);
2811 laser.redraw = FALSE;
2816 if (game_mm.num_pacman && FrameReached(&pacman_delay, PACMAN_MOVE_DELAY))
2820 if (laser.num_damages > MAX_LASER_LEN && !laser.fuse_off)
2822 DrawLaser(0, DL_LASER_DISABLED);
2827 if (FrameReached(&energy_delay, ENERGY_DELAY))
2829 game_mm.energy_left--;
2830 if (game_mm.energy_left >= 0)
2833 BlitBitmap(pix[PIX_DOOR], drawto,
2834 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
2835 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
2836 DX_ENERGY, DY_ENERGY);
2838 redraw_mask |= REDRAW_DOOR_1;
2840 else if (setup.time_limit)
2844 for (i = 15; i >= 0; i--)
2847 SetRGB(pen_ray, 0x0000, 0x0000, i * color_scale);
2849 pen_ray = GetPixelFromRGB(window,
2850 native_mm_level.laser_red * 0x11 * i,
2851 native_mm_level.laser_green * 0x11 * i,
2852 native_mm_level.laser_blue * 0x11 * i);
2854 DrawLaser(0, DL_LASER_ENABLED);
2859 StopSound_MM(SND_MM_GAME_HEALTH_CHARGING);
2862 DrawLaser(0, DL_LASER_DISABLED);
2863 game_mm.game_over = TRUE;
2864 game_mm.game_over_cause = GAME_OVER_NO_ENERGY;
2867 if (Request("Out of magic energy ! Play it again ?",
2868 REQ_ASK | REQ_STAY_CLOSED))
2874 game_status = MAINMENU;
2883 element = laser.dest_element;
2886 if (element != Feld[ELX][ELY])
2888 printf("element == %d, Feld[ELX][ELY] == %d\n",
2889 element, Feld[ELX][ELY]);
2893 if (!laser.overloaded && laser.overload_value == 0 &&
2894 element != EL_BOMB &&
2895 element != EL_MINE &&
2896 element != EL_BALL_GRAY &&
2897 element != EL_BLOCK_STONE &&
2898 element != EL_BLOCK_WOOD &&
2899 element != EL_FUSE_ON &&
2900 element != EL_FUEL_FULL &&
2901 !IS_WALL_ICE(element) &&
2902 !IS_WALL_AMOEBA(element))
2905 if (((laser.overloaded && laser.overload_value < MAX_LASER_OVERLOAD) ||
2906 (!laser.overloaded && laser.overload_value > 0)) &&
2907 FrameReached(&overload_delay, HEALTH_DELAY(laser.overloaded)))
2909 if (laser.overloaded)
2910 laser.overload_value++;
2912 laser.overload_value--;
2914 if (game_mm.cheat_no_overload)
2916 laser.overloaded = FALSE;
2917 laser.overload_value = 0;
2920 game_mm.laser_overload_value = laser.overload_value;
2922 if (laser.overload_value < MAX_LASER_OVERLOAD - 8)
2924 int color_up = 0xFF * laser.overload_value / MAX_LASER_OVERLOAD;
2925 int color_down = 0xFF - color_up;
2928 SetRGB(pen_ray, (laser.overload_value / 6) * color_scale, 0x0000,
2929 (15 - (laser.overload_value / 6)) * color_scale);
2932 GetPixelFromRGB(window,
2933 (native_mm_level.laser_red ? 0xFF : color_up),
2934 (native_mm_level.laser_green ? color_down : 0x00),
2935 (native_mm_level.laser_blue ? color_down : 0x00));
2937 DrawLaser(0, DL_LASER_ENABLED);
2941 if (!laser.overloaded)
2942 StopSound_MM(SND_MM_GAME_HEALTH_CHARGING);
2943 else if (setup.sound_loops)
2944 PlaySoundLoop_MM(SND_MM_GAME_HEALTH_CHARGING);
2946 PlaySound_MM(SND_MM_GAME_HEALTH_CHARGING);
2948 if (laser.overloaded)
2951 BlitBitmap(pix[PIX_DOOR], drawto,
2952 DOOR_GFX_PAGEX4 + XX_OVERLOAD,
2953 DOOR_GFX_PAGEY1 + YY_OVERLOAD + OVERLOAD_YSIZE
2954 - laser.overload_value,
2955 OVERLOAD_XSIZE, laser.overload_value,
2956 DX_OVERLOAD, DY_OVERLOAD + OVERLOAD_YSIZE
2957 - laser.overload_value);
2959 redraw_mask |= REDRAW_DOOR_1;
2964 BlitBitmap(pix[PIX_DOOR], drawto,
2965 DOOR_GFX_PAGEX5 + XX_OVERLOAD, DOOR_GFX_PAGEY1 + YY_OVERLOAD,
2966 OVERLOAD_XSIZE, OVERLOAD_YSIZE - laser.overload_value,
2967 DX_OVERLOAD, DY_OVERLOAD);
2969 redraw_mask |= REDRAW_DOOR_1;
2972 if (laser.overload_value == MAX_LASER_OVERLOAD)
2976 for (i = 15; i >= 0; i--)
2979 SetRGB(pen_ray, i * color_scale, 0x0000, 0x0000);
2982 pen_ray = GetPixelFromRGB(window, 0x11 * i, 0x00, 0x00);
2984 DrawLaser(0, DL_LASER_ENABLED);
2989 DrawLaser(0, DL_LASER_DISABLED);
2991 game_mm.game_over = TRUE;
2992 game_mm.game_over_cause = GAME_OVER_OVERLOADED;
2995 if (Request("Magic spell hit Mc Duffin ! Play it again ?",
2996 REQ_ASK | REQ_STAY_CLOSED))
3002 game_status = MAINMENU;
3016 if (element == EL_BOMB && CT > 1500)
3018 if (game_mm.cheat_no_explosion)
3022 laser.num_damages--;
3023 DrawLaser(0, DL_LASER_DISABLED);
3024 laser.num_edges = 0;
3029 laser.dest_element = EL_EXPLODING_OPAQUE;
3033 laser.num_damages--;
3034 DrawLaser(0, DL_LASER_DISABLED);
3036 laser.num_edges = 0;
3037 Bang_MM(laser.start_edge.x, laser.start_edge.y);
3039 if (Request("Bomb killed Mc Duffin ! Play it again ?",
3040 REQ_ASK | REQ_STAY_CLOSED))
3046 game_status = MAINMENU;
3054 if (element == EL_FUSE_ON && CT > 500)
3056 laser.fuse_off = TRUE;
3060 DrawLaser(0, DL_LASER_DISABLED);
3061 DrawGraphic_MM(ELX, ELY, IMG_MM_FUSE);
3064 if (element == EL_BALL_GRAY && CT > 1500)
3066 static int new_elements[] =
3069 EL_MIRROR_FIXED_START,
3071 EL_POLAR_CROSS_START,
3077 int num_new_elements = sizeof(new_elements) / sizeof(int);
3078 int new_element = new_elements[RND(num_new_elements)];
3080 Store[ELX][ELY] = new_element + RND(get_num_elements(new_element));
3081 Feld[ELX][ELY] = EL_GRAY_BALL_OPENING;
3083 /* !!! CHECK AGAIN: Laser on Polarizer !!! */
3094 element = EL_MIRROR_START + RND(16);
3100 element = (rnd == 0 ? EL_KETTLE : rnd == 1 ? EL_BOMB : EL_PRISM);
3107 element = (rnd == 0 ? EL_FUSE_ON :
3108 rnd >= 1 && rnd <= 4 ? EL_PACMAN_RIGHT + rnd - 1 :
3109 rnd >= 5 && rnd <= 20 ? EL_POLAR_START + rnd - 5 :
3110 rnd >= 21 && rnd <= 24 ? EL_POLAR_CROSS_START + rnd - 21 :
3111 EL_MIRROR_FIXED_START + rnd - 25);
3116 graphic = el2gfx(element);
3118 for (i = 0; i < 50; i++)
3124 BlitBitmap(pix[PIX_BACK], drawto,
3125 SX + (graphic % GFX_PER_LINE) * TILEX + x,
3126 SY + (graphic / GFX_PER_LINE) * TILEY + y, 6, 6,
3127 SX + ELX * TILEX + x,
3128 SY + ELY * TILEY + y);
3130 MarkTileDirty(ELX, ELY);
3133 DrawLaser(0, DL_LASER_ENABLED);
3138 Feld[ELX][ELY] = element;
3139 DrawField_MM(ELX, ELY);
3142 printf("NEW ELEMENT: (%d, %d)\n", ELX, ELY);
3145 /* above stuff: GRAY BALL -> PRISM !!! */
3147 LX = ELX * TILEX + 14;
3148 LY = ELY * TILEY + 14;
3149 if (laser.current_angle == (laser.current_angle >> 1) << 1)
3156 laser.num_edges -= 2;
3157 laser.num_damages--;
3161 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
3162 if (laser.damage[i].is_mirror)
3166 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3168 DrawLaser(0, DL_LASER_DISABLED);
3170 DrawLaser(0, DL_LASER_DISABLED);
3176 printf("TEST ELEMENT: %d\n", Feld[0][0]);
3183 if (IS_WALL_ICE(element) && CT > 1000)
3185 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_SHRINKING);
3188 Feld[ELX][ELY] = Feld[ELX][ELY] - EL_WALL_ICE + EL_WALL_CHANGING;
3189 Store[ELX][ELY] = EL_WALL_ICE;
3190 Store2[ELX][ELY] = laser.wall_mask;
3192 laser.dest_element = Feld[ELX][ELY];
3197 for (i = 0; i < 5; i++)
3203 Feld[ELX][ELY] &= (laser.wall_mask ^ 0xFF);
3207 DrawWallsAnimation_MM(ELX, ELY, Feld[ELX][ELY], phase, laser.wall_mask);
3212 if (Feld[ELX][ELY] == EL_WALL_ICE)
3213 Feld[ELX][ELY] = EL_EMPTY;
3217 LX = laser.edge[laser.num_edges].x - (SX + 2);
3218 LY = laser.edge[laser.num_edges].y - (SY + 2);
3221 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
3222 if (laser.damage[i].is_mirror)
3226 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3228 DrawLaser(0, DL_LASER_DISABLED);
3235 if (IS_WALL_AMOEBA(element) && CT > 1200)
3237 int k1, k2, k3, dx, dy, de, dm;
3238 int element2 = Feld[ELX][ELY];
3240 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element))
3243 for (i = laser.num_damages - 1; i >= 0; i--)
3244 if (laser.damage[i].is_mirror)
3247 r = laser.num_edges;
3248 d = laser.num_damages;
3255 DrawLaser(laser.damage[k1].edge - 1, DL_LASER_DISABLED);
3258 DrawLaser(0, DL_LASER_ENABLED);
3261 x = laser.damage[k1].x;
3262 y = laser.damage[k1].y;
3267 for (i = 0; i < 4; i++)
3269 if (laser.wall_mask & (1 << i))
3271 if (ReadPixel(drawto,
3272 SX + ELX * TILEX + 14 + (i % 2) * 2,
3273 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3275 if (ReadPixel(drawto,
3276 SX + ELX * TILEX + 31 * (i % 2),
3277 SY + ELY * TILEY + 14 + (i / 2) * 2) == pen_ray)
3284 for (i = 0; i < 4; i++)
3286 if (laser.wall_mask & (1 << i))
3288 if (ReadPixel(drawto,
3289 SX + ELX * TILEX + 31 * (i % 2),
3290 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3297 if (laser.num_beamers > 0 ||
3298 k1 < 1 || k2 < 4 || k3 < 4 ||
3299 ReadPixel(drawto, SX + ELX * TILEX + 14, SY + ELY * TILEY + 14)
3302 laser.num_edges = r;
3303 laser.num_damages = d;
3305 DrawLaser(0, DL_LASER_DISABLED);
3308 Feld[ELX][ELY] = element | laser.wall_mask;
3312 de = Feld[ELX][ELY];
3313 dm = laser.wall_mask;
3317 int x = ELX, y = ELY;
3318 int wall_mask = laser.wall_mask;
3321 DrawLaser(0, DL_LASER_ENABLED);
3323 PlayLevelSound_MM(dx, dy, element, MM_ACTION_GROWING);
3325 Feld[x][y] = Feld[x][y] - EL_WALL_AMOEBA + EL_WALL_CHANGING;
3326 Store[x][y] = EL_WALL_AMOEBA;
3327 Store2[x][y] = wall_mask;
3333 DrawWallsAnimation_MM(dx, dy, de, 4, dm);
3335 DrawLaser(0, DL_LASER_ENABLED);
3337 PlayLevelSound_MM(dx, dy, element, MM_ACTION_GROWING);
3339 for (i = 4; i >= 0; i--)
3341 DrawWallsAnimation_MM(dx, dy, de, i, dm);
3347 DrawLaser(0, DL_LASER_ENABLED);
3352 if ((element == EL_BLOCK_WOOD || element == EL_BLOCK_STONE) &&
3353 laser.stops_inside_element && CT > 1500)
3358 if (ABS(XS) > ABS(YS))
3365 for (i = 0; i < 4; i++)
3372 x = ELX + Step[k * 4].x;
3373 y = ELY + Step[k * 4].y;
3375 if (!IN_LEV_FIELD(x, y) || Feld[x][y] != EL_EMPTY)
3378 if (ObjHit(x, y, HIT_POS_CENTER | HIT_POS_EDGE | HIT_POS_BETWEEN))
3386 laser.overloaded = (element == EL_BLOCK_STONE);
3391 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_PUSHING);
3394 Feld[x][y] = element;
3396 DrawGraphic_MM(ELX, ELY, IMG_EMPTY);
3399 if (element == EL_BLOCK_STONE && Box[ELX][ELY])
3401 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
3402 DrawLaser(laser.num_edges - 1, DL_LASER_ENABLED);
3410 if (element == EL_FUEL_FULL && CT > 200)
3412 for (i = game_mm.energy_left; i <= MAX_LASER_ENERGY; i+=2)
3415 BlitBitmap(pix[PIX_DOOR], drawto,
3416 DOOR_GFX_PAGEX4 + XX_ENERGY,
3417 DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
3418 ENERGY_XSIZE, i, DX_ENERGY,
3419 DY_ENERGY + ENERGY_YSIZE - i);
3422 redraw_mask |= REDRAW_DOOR_1;
3428 game_mm.energy_left = MAX_LASER_ENERGY;
3429 Feld[ELX][ELY] = EL_FUEL_EMPTY;
3430 DrawField_MM(ELX, ELY);
3432 DrawLaser(0, DL_LASER_ENABLED);
3440 void GameActions_MM(struct MouseActionInfo action, boolean warp_mode)
3442 ClickElement(action.lx, action.ly, action.button);
3444 GameActions_MM_Ext(action, warp_mode);
3450 int mx, my, ox, oy, nx, ny;
3454 if (++p >= game_mm.num_pacman)
3457 game_mm.pacman[p].dir--;
3459 for (l = 1; l < 5; l++)
3461 game_mm.pacman[p].dir++;
3463 if (game_mm.pacman[p].dir > 4)
3464 game_mm.pacman[p].dir = 1;
3466 if (game_mm.pacman[p].dir % 2)
3469 my = game_mm.pacman[p].dir - 2;
3474 mx = 3 - game_mm.pacman[p].dir;
3477 ox = game_mm.pacman[p].x;
3478 oy = game_mm.pacman[p].y;
3481 element = Feld[nx][ny];
3483 if (nx < 0 || nx > 15 || ny < 0 || ny > 11)
3486 if (!IS_EATABLE4PACMAN(element))
3489 if (ObjHit(nx, ny, HIT_POS_CENTER))
3492 Feld[ox][oy] = EL_EMPTY;
3494 EL_PACMAN_RIGHT - 1 +
3495 (game_mm.pacman[p].dir - 1 +
3496 (game_mm.pacman[p].dir % 2) * 2);
3498 game_mm.pacman[p].x = nx;
3499 game_mm.pacman[p].y = ny;
3501 DrawGraphic_MM(ox, oy, IMG_EMPTY);
3503 if (element != EL_EMPTY)
3505 int graphic = el2gfx(Feld[nx][ny]);
3510 getGraphicSource(graphic, 0, &bitmap, &src_x, &src_y);
3513 ox = SX + ox * TILEX;
3514 oy = SY + oy * TILEY;
3516 for (i = 1; i < 33; i += 2)
3517 BlitBitmap(bitmap, window,
3518 src_x, src_y, TILEX, TILEY,
3519 ox + i * mx, oy + i * my);
3520 Ct = Ct + Counter() - CT;
3523 DrawField_MM(nx, ny);
3526 if (!laser.fuse_off)
3528 DrawLaser(0, DL_LASER_ENABLED);
3530 if (ObjHit(nx, ny, HIT_POS_BETWEEN))
3532 AddDamagedField(nx, ny);
3534 laser.damage[laser.num_damages - 1].edge = 0;
3538 if (element == EL_BOMB)
3539 DeletePacMan(nx, ny);
3541 if (IS_WALL_AMOEBA(element) &&
3542 (LX + 2 * XS) / TILEX == nx &&
3543 (LY + 2 * YS) / TILEY == ny)
3556 boolean raise_level = FALSE;
3559 if (local_player->MovPos)
3562 local_player->LevelSolved = FALSE;
3565 if (game_mm.energy_left)
3567 if (setup.sound_loops)
3568 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3569 SND_CTRL_PLAY_LOOP);
3571 while (game_mm.energy_left > 0)
3573 if (!setup.sound_loops)
3574 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3577 if (game_mm.energy_left > 0 && !(game_mm.energy_left % 10))
3578 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3583 game_mm.energy_left--;
3584 if (game_mm.energy_left >= 0)
3587 BlitBitmap(pix[PIX_DOOR], drawto,
3588 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
3589 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
3590 DX_ENERGY, DY_ENERGY);
3592 redraw_mask |= REDRAW_DOOR_1;
3599 if (setup.sound_loops)
3600 StopSound(SND_SIRR);
3602 else if (native_mm_level.time == 0) /* level without time limit */
3604 if (setup.sound_loops)
3605 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3606 SND_CTRL_PLAY_LOOP);
3608 while (TimePlayed < 999)
3610 if (!setup.sound_loops)
3611 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3612 if (TimePlayed < 999 && !(TimePlayed % 10))
3613 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3614 if (TimePlayed < 900 && !(TimePlayed % 10))
3620 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
3627 if (setup.sound_loops)
3628 StopSound(SND_SIRR);
3635 CloseDoor(DOOR_CLOSE_1);
3637 Request("Level solved !", REQ_CONFIRM);
3639 if (level_nr == leveldir_current->handicap_level)
3641 leveldir_current->handicap_level++;
3642 SaveLevelSetup_SeriesInfo();
3645 if (level_editor_test_game)
3646 game_mm.score = -1; /* no highscore when playing from editor */
3647 else if (level_nr < leveldir_current->last_level)
3648 raise_level = TRUE; /* advance to next level */
3650 if ((hi_pos = NewHiScore_MM()) >= 0)
3652 game_status = HALLOFFAME;
3654 // DrawHallOfFame(hi_pos);
3661 game_status = MAINMENU;
3677 // LoadScore(level_nr);
3679 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
3680 game_mm.score < highscore[MAX_SCORE_ENTRIES - 1].Score)
3683 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3685 if (game_mm.score > highscore[k].Score)
3687 /* player has made it to the hall of fame */
3689 if (k < MAX_SCORE_ENTRIES - 1)
3691 int m = MAX_SCORE_ENTRIES - 1;
3694 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3695 if (!strcmp(setup.player_name, highscore[l].Name))
3697 if (m == k) /* player's new highscore overwrites his old one */
3701 for (l = m; l>k; l--)
3703 strcpy(highscore[l].Name, highscore[l - 1].Name);
3704 highscore[l].Score = highscore[l - 1].Score;
3711 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3712 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3713 highscore[k].Score = game_mm.score;
3720 else if (!strncmp(setup.player_name, highscore[k].Name,
3721 MAX_PLAYER_NAME_LEN))
3722 break; /* player already there with a higher score */
3727 // if (position >= 0)
3728 // SaveScore(level_nr);
3733 static void InitMovingField_MM(int x, int y, int direction)
3735 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3736 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3738 MovDir[x][y] = direction;
3739 MovDir[newx][newy] = direction;
3741 if (Feld[newx][newy] == EL_EMPTY)
3742 Feld[newx][newy] = EL_BLOCKED;
3745 static void Moving2Blocked_MM(int x, int y, int *goes_to_x, int *goes_to_y)
3747 int direction = MovDir[x][y];
3748 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3749 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3755 static void Blocked2Moving_MM(int x, int y,
3756 int *comes_from_x, int *comes_from_y)
3758 int oldx = x, oldy = y;
3759 int direction = MovDir[x][y];
3761 if (direction == MV_LEFT)
3763 else if (direction == MV_RIGHT)
3765 else if (direction == MV_UP)
3767 else if (direction == MV_DOWN)
3770 *comes_from_x = oldx;
3771 *comes_from_y = oldy;
3774 static int MovingOrBlocked2Element_MM(int x, int y)
3776 int element = Feld[x][y];
3778 if (element == EL_BLOCKED)
3782 Blocked2Moving_MM(x, y, &oldx, &oldy);
3784 return Feld[oldx][oldy];
3791 static void RemoveField(int x, int y)
3793 Feld[x][y] = EL_EMPTY;
3800 static void RemoveMovingField_MM(int x, int y)
3802 int oldx = x, oldy = y, newx = x, newy = y;
3804 if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
3807 if (IS_MOVING(x, y))
3809 Moving2Blocked_MM(x, y, &newx, &newy);
3810 if (Feld[newx][newy] != EL_BLOCKED)
3813 else if (Feld[x][y] == EL_BLOCKED)
3815 Blocked2Moving_MM(x, y, &oldx, &oldy);
3816 if (!IS_MOVING(oldx, oldy))
3820 Feld[oldx][oldy] = EL_EMPTY;
3821 Feld[newx][newy] = EL_EMPTY;
3822 MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
3823 MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
3825 DrawLevelField_MM(oldx, oldy);
3826 DrawLevelField_MM(newx, newy);
3829 void PlaySoundLevel(int x, int y, int sound_nr)
3831 int sx = SCREENX(x), sy = SCREENY(y);
3833 int silence_distance = 8;
3835 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
3836 (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
3839 if (!IN_LEV_FIELD(x, y) ||
3840 sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
3841 sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
3844 volume = SOUND_MAX_VOLUME;
3847 stereo = (sx - SCR_FIELDX/2) * 12;
3849 stereo = SOUND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
3850 if (stereo > SOUND_MAX_RIGHT)
3851 stereo = SOUND_MAX_RIGHT;
3852 if (stereo < SOUND_MAX_LEFT)
3853 stereo = SOUND_MAX_LEFT;
3856 if (!IN_SCR_FIELD(sx, sy))
3858 int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
3859 int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
3861 volume -= volume * (dx > dy ? dx : dy) / silence_distance;
3864 PlaySoundExt(sound_nr, volume, stereo, SND_CTRL_PLAY_SOUND);
3867 static void RaiseScore_MM(int value)
3869 game_mm.score += value;
3872 DrawText(DX_SCORE, DY_SCORE, int2str(game_mm.score, 4),
3877 void RaiseScoreElement_MM(int element)
3882 RaiseScore_MM(native_mm_level.score[SC_PACMAN]);
3886 RaiseScore_MM(native_mm_level.score[SC_KEY]);