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);
2943 if (!laser.overloaded)
2944 StopSound_MM(SND_MM_GAME_HEALTH_CHARGING);
2945 else if (setup.sound_loops)
2946 PlaySoundLoop_MM(SND_MM_GAME_HEALTH_CHARGING);
2948 PlaySound_MM(SND_MM_GAME_HEALTH_CHARGING);
2950 if (laser.overloaded)
2953 BlitBitmap(pix[PIX_DOOR], drawto,
2954 DOOR_GFX_PAGEX4 + XX_OVERLOAD,
2955 DOOR_GFX_PAGEY1 + YY_OVERLOAD + OVERLOAD_YSIZE
2956 - laser.overload_value,
2957 OVERLOAD_XSIZE, laser.overload_value,
2958 DX_OVERLOAD, DY_OVERLOAD + OVERLOAD_YSIZE
2959 - laser.overload_value);
2961 redraw_mask |= REDRAW_DOOR_1;
2966 BlitBitmap(pix[PIX_DOOR], drawto,
2967 DOOR_GFX_PAGEX5 + XX_OVERLOAD, DOOR_GFX_PAGEY1 + YY_OVERLOAD,
2968 OVERLOAD_XSIZE, OVERLOAD_YSIZE - laser.overload_value,
2969 DX_OVERLOAD, DY_OVERLOAD);
2971 redraw_mask |= REDRAW_DOOR_1;
2974 if (laser.overload_value == MAX_LASER_OVERLOAD)
2978 for (i = 15; i >= 0; i--)
2981 SetRGB(pen_ray, i * color_scale, 0x0000, 0x0000);
2984 pen_ray = GetPixelFromRGB(window, 0x11 * i, 0x00, 0x00);
2986 DrawLaser(0, DL_LASER_ENABLED);
2991 DrawLaser(0, DL_LASER_DISABLED);
2993 game_mm.game_over = TRUE;
2994 game_mm.game_over_cause = GAME_OVER_OVERLOADED;
2997 if (Request("Magic spell hit Mc Duffin ! Play it again ?",
2998 REQ_ASK | REQ_STAY_CLOSED))
3004 game_status = MAINMENU;
3018 if (element == EL_BOMB && CT > 1500)
3020 if (game_mm.cheat_no_explosion)
3024 laser.num_damages--;
3025 DrawLaser(0, DL_LASER_DISABLED);
3026 laser.num_edges = 0;
3031 laser.dest_element = EL_EXPLODING_OPAQUE;
3035 laser.num_damages--;
3036 DrawLaser(0, DL_LASER_DISABLED);
3038 laser.num_edges = 0;
3039 Bang_MM(laser.start_edge.x, laser.start_edge.y);
3041 if (Request("Bomb killed Mc Duffin ! Play it again ?",
3042 REQ_ASK | REQ_STAY_CLOSED))
3048 game_status = MAINMENU;
3056 if (element == EL_FUSE_ON && CT > 500)
3058 laser.fuse_off = TRUE;
3062 DrawLaser(0, DL_LASER_DISABLED);
3063 DrawGraphic_MM(ELX, ELY, IMG_MM_FUSE);
3066 if (element == EL_BALL_GRAY && CT > 1500)
3068 static int new_elements[] =
3071 EL_MIRROR_FIXED_START,
3073 EL_POLAR_CROSS_START,
3079 int num_new_elements = sizeof(new_elements) / sizeof(int);
3080 int new_element = new_elements[RND(num_new_elements)];
3082 Store[ELX][ELY] = new_element + RND(get_num_elements(new_element));
3083 Feld[ELX][ELY] = EL_GRAY_BALL_OPENING;
3085 /* !!! CHECK AGAIN: Laser on Polarizer !!! */
3096 element = EL_MIRROR_START + RND(16);
3102 element = (rnd == 0 ? EL_KETTLE : rnd == 1 ? EL_BOMB : EL_PRISM);
3109 element = (rnd == 0 ? EL_FUSE_ON :
3110 rnd >= 1 && rnd <= 4 ? EL_PACMAN_RIGHT + rnd - 1 :
3111 rnd >= 5 && rnd <= 20 ? EL_POLAR_START + rnd - 5 :
3112 rnd >= 21 && rnd <= 24 ? EL_POLAR_CROSS_START + rnd - 21 :
3113 EL_MIRROR_FIXED_START + rnd - 25);
3118 graphic = el2gfx(element);
3120 for (i = 0; i < 50; i++)
3126 BlitBitmap(pix[PIX_BACK], drawto,
3127 SX + (graphic % GFX_PER_LINE) * TILEX + x,
3128 SY + (graphic / GFX_PER_LINE) * TILEY + y, 6, 6,
3129 SX + ELX * TILEX + x,
3130 SY + ELY * TILEY + y);
3132 MarkTileDirty(ELX, ELY);
3135 DrawLaser(0, DL_LASER_ENABLED);
3140 Feld[ELX][ELY] = element;
3141 DrawField_MM(ELX, ELY);
3144 printf("NEW ELEMENT: (%d, %d)\n", ELX, ELY);
3147 /* above stuff: GRAY BALL -> PRISM !!! */
3149 LX = ELX * TILEX + 14;
3150 LY = ELY * TILEY + 14;
3151 if (laser.current_angle == (laser.current_angle >> 1) << 1)
3158 laser.num_edges -= 2;
3159 laser.num_damages--;
3163 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
3164 if (laser.damage[i].is_mirror)
3168 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3170 DrawLaser(0, DL_LASER_DISABLED);
3172 DrawLaser(0, DL_LASER_DISABLED);
3178 printf("TEST ELEMENT: %d\n", Feld[0][0]);
3185 if (IS_WALL_ICE(element) && CT > 1000)
3187 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_SHRINKING);
3190 Feld[ELX][ELY] = Feld[ELX][ELY] - EL_WALL_ICE + EL_WALL_CHANGING;
3191 Store[ELX][ELY] = EL_WALL_ICE;
3192 Store2[ELX][ELY] = laser.wall_mask;
3194 laser.dest_element = Feld[ELX][ELY];
3199 for (i = 0; i < 5; i++)
3205 Feld[ELX][ELY] &= (laser.wall_mask ^ 0xFF);
3209 DrawWallsAnimation_MM(ELX, ELY, Feld[ELX][ELY], phase, laser.wall_mask);
3214 if (Feld[ELX][ELY] == EL_WALL_ICE)
3215 Feld[ELX][ELY] = EL_EMPTY;
3219 LX = laser.edge[laser.num_edges].x - (SX + 2);
3220 LY = laser.edge[laser.num_edges].y - (SY + 2);
3223 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
3224 if (laser.damage[i].is_mirror)
3228 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3230 DrawLaser(0, DL_LASER_DISABLED);
3237 if (IS_WALL_AMOEBA(element) && CT > 1200)
3239 int k1, k2, k3, dx, dy, de, dm;
3240 int element2 = Feld[ELX][ELY];
3242 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element))
3245 for (i = laser.num_damages - 1; i >= 0; i--)
3246 if (laser.damage[i].is_mirror)
3249 r = laser.num_edges;
3250 d = laser.num_damages;
3257 DrawLaser(laser.damage[k1].edge - 1, DL_LASER_DISABLED);
3260 DrawLaser(0, DL_LASER_ENABLED);
3263 x = laser.damage[k1].x;
3264 y = laser.damage[k1].y;
3269 for (i = 0; i < 4; i++)
3271 if (laser.wall_mask & (1 << i))
3273 if (ReadPixel(drawto,
3274 SX + ELX * TILEX + 14 + (i % 2) * 2,
3275 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3277 if (ReadPixel(drawto,
3278 SX + ELX * TILEX + 31 * (i % 2),
3279 SY + ELY * TILEY + 14 + (i / 2) * 2) == pen_ray)
3286 for (i = 0; i < 4; i++)
3288 if (laser.wall_mask & (1 << i))
3290 if (ReadPixel(drawto,
3291 SX + ELX * TILEX + 31 * (i % 2),
3292 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3299 if (laser.num_beamers > 0 ||
3300 k1 < 1 || k2 < 4 || k3 < 4 ||
3301 ReadPixel(drawto, SX + ELX * TILEX + 14, SY + ELY * TILEY + 14)
3304 laser.num_edges = r;
3305 laser.num_damages = d;
3307 DrawLaser(0, DL_LASER_DISABLED);
3310 Feld[ELX][ELY] = element | laser.wall_mask;
3314 de = Feld[ELX][ELY];
3315 dm = laser.wall_mask;
3319 int x = ELX, y = ELY;
3320 int wall_mask = laser.wall_mask;
3323 DrawLaser(0, DL_LASER_ENABLED);
3325 PlayLevelSound_MM(dx, dy, element, MM_ACTION_GROWING);
3327 Feld[x][y] = Feld[x][y] - EL_WALL_AMOEBA + EL_WALL_CHANGING;
3328 Store[x][y] = EL_WALL_AMOEBA;
3329 Store2[x][y] = wall_mask;
3335 DrawWallsAnimation_MM(dx, dy, de, 4, dm);
3337 DrawLaser(0, DL_LASER_ENABLED);
3339 PlayLevelSound_MM(dx, dy, element, MM_ACTION_GROWING);
3341 for (i = 4; i >= 0; i--)
3343 DrawWallsAnimation_MM(dx, dy, de, i, dm);
3349 DrawLaser(0, DL_LASER_ENABLED);
3354 if ((element == EL_BLOCK_WOOD || element == EL_BLOCK_STONE) &&
3355 laser.stops_inside_element && CT > 1500)
3360 if (ABS(XS) > ABS(YS))
3367 for (i = 0; i < 4; i++)
3374 x = ELX + Step[k * 4].x;
3375 y = ELY + Step[k * 4].y;
3377 if (!IN_LEV_FIELD(x, y) || Feld[x][y] != EL_EMPTY)
3380 if (ObjHit(x, y, HIT_POS_CENTER | HIT_POS_EDGE | HIT_POS_BETWEEN))
3388 laser.overloaded = (element == EL_BLOCK_STONE);
3393 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_PUSHING);
3396 Feld[x][y] = element;
3398 DrawGraphic_MM(ELX, ELY, IMG_EMPTY);
3401 if (element == EL_BLOCK_STONE && Box[ELX][ELY])
3403 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
3404 DrawLaser(laser.num_edges - 1, DL_LASER_ENABLED);
3412 if (element == EL_FUEL_FULL && CT > 200)
3414 for (i = game_mm.energy_left; i <= MAX_LASER_ENERGY; i+=2)
3417 BlitBitmap(pix[PIX_DOOR], drawto,
3418 DOOR_GFX_PAGEX4 + XX_ENERGY,
3419 DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
3420 ENERGY_XSIZE, i, DX_ENERGY,
3421 DY_ENERGY + ENERGY_YSIZE - i);
3424 redraw_mask |= REDRAW_DOOR_1;
3430 game_mm.energy_left = MAX_LASER_ENERGY;
3431 Feld[ELX][ELY] = EL_FUEL_EMPTY;
3432 DrawField_MM(ELX, ELY);
3434 DrawLaser(0, DL_LASER_ENABLED);
3442 void GameActions_MM(struct MouseActionInfo action, boolean warp_mode)
3444 ClickElement(action.lx, action.ly, action.button);
3446 GameActions_MM_Ext(action, warp_mode);
3452 int mx, my, ox, oy, nx, ny;
3456 if (++p >= game_mm.num_pacman)
3459 game_mm.pacman[p].dir--;
3461 for (l = 1; l < 5; l++)
3463 game_mm.pacman[p].dir++;
3465 if (game_mm.pacman[p].dir > 4)
3466 game_mm.pacman[p].dir = 1;
3468 if (game_mm.pacman[p].dir % 2)
3471 my = game_mm.pacman[p].dir - 2;
3476 mx = 3 - game_mm.pacman[p].dir;
3479 ox = game_mm.pacman[p].x;
3480 oy = game_mm.pacman[p].y;
3483 element = Feld[nx][ny];
3485 if (nx < 0 || nx > 15 || ny < 0 || ny > 11)
3488 if (!IS_EATABLE4PACMAN(element))
3491 if (ObjHit(nx, ny, HIT_POS_CENTER))
3494 Feld[ox][oy] = EL_EMPTY;
3496 EL_PACMAN_RIGHT - 1 +
3497 (game_mm.pacman[p].dir - 1 +
3498 (game_mm.pacman[p].dir % 2) * 2);
3500 game_mm.pacman[p].x = nx;
3501 game_mm.pacman[p].y = ny;
3503 DrawGraphic_MM(ox, oy, IMG_EMPTY);
3505 if (element != EL_EMPTY)
3507 int graphic = el2gfx(Feld[nx][ny]);
3512 getGraphicSource(graphic, 0, &bitmap, &src_x, &src_y);
3515 ox = SX + ox * TILEX;
3516 oy = SY + oy * TILEY;
3518 for (i = 1; i < 33; i += 2)
3519 BlitBitmap(bitmap, window,
3520 src_x, src_y, TILEX, TILEY,
3521 ox + i * mx, oy + i * my);
3522 Ct = Ct + Counter() - CT;
3525 DrawField_MM(nx, ny);
3528 if (!laser.fuse_off)
3530 DrawLaser(0, DL_LASER_ENABLED);
3532 if (ObjHit(nx, ny, HIT_POS_BETWEEN))
3534 AddDamagedField(nx, ny);
3536 laser.damage[laser.num_damages - 1].edge = 0;
3540 if (element == EL_BOMB)
3541 DeletePacMan(nx, ny);
3543 if (IS_WALL_AMOEBA(element) &&
3544 (LX + 2 * XS) / TILEX == nx &&
3545 (LY + 2 * YS) / TILEY == ny)
3558 boolean raise_level = FALSE;
3561 if (local_player->MovPos)
3564 local_player->LevelSolved = FALSE;
3567 if (game_mm.energy_left)
3569 if (setup.sound_loops)
3570 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3571 SND_CTRL_PLAY_LOOP);
3573 while (game_mm.energy_left > 0)
3575 if (!setup.sound_loops)
3576 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3579 if (game_mm.energy_left > 0 && !(game_mm.energy_left % 10))
3580 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3585 game_mm.energy_left--;
3586 if (game_mm.energy_left >= 0)
3589 BlitBitmap(pix[PIX_DOOR], drawto,
3590 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
3591 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
3592 DX_ENERGY, DY_ENERGY);
3594 redraw_mask |= REDRAW_DOOR_1;
3601 if (setup.sound_loops)
3602 StopSound(SND_SIRR);
3604 else if (native_mm_level.time == 0) /* level without time limit */
3606 if (setup.sound_loops)
3607 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3608 SND_CTRL_PLAY_LOOP);
3610 while (TimePlayed < 999)
3612 if (!setup.sound_loops)
3613 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3614 if (TimePlayed < 999 && !(TimePlayed % 10))
3615 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3616 if (TimePlayed < 900 && !(TimePlayed % 10))
3622 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
3629 if (setup.sound_loops)
3630 StopSound(SND_SIRR);
3637 CloseDoor(DOOR_CLOSE_1);
3639 Request("Level solved !", REQ_CONFIRM);
3641 if (level_nr == leveldir_current->handicap_level)
3643 leveldir_current->handicap_level++;
3644 SaveLevelSetup_SeriesInfo();
3647 if (level_editor_test_game)
3648 game_mm.score = -1; /* no highscore when playing from editor */
3649 else if (level_nr < leveldir_current->last_level)
3650 raise_level = TRUE; /* advance to next level */
3652 if ((hi_pos = NewHiScore_MM()) >= 0)
3654 game_status = HALLOFFAME;
3656 // DrawHallOfFame(hi_pos);
3663 game_status = MAINMENU;
3679 // LoadScore(level_nr);
3681 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
3682 game_mm.score < highscore[MAX_SCORE_ENTRIES - 1].Score)
3685 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3687 if (game_mm.score > highscore[k].Score)
3689 /* player has made it to the hall of fame */
3691 if (k < MAX_SCORE_ENTRIES - 1)
3693 int m = MAX_SCORE_ENTRIES - 1;
3696 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3697 if (!strcmp(setup.player_name, highscore[l].Name))
3699 if (m == k) /* player's new highscore overwrites his old one */
3703 for (l = m; l>k; l--)
3705 strcpy(highscore[l].Name, highscore[l - 1].Name);
3706 highscore[l].Score = highscore[l - 1].Score;
3713 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3714 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3715 highscore[k].Score = game_mm.score;
3722 else if (!strncmp(setup.player_name, highscore[k].Name,
3723 MAX_PLAYER_NAME_LEN))
3724 break; /* player already there with a higher score */
3729 // if (position >= 0)
3730 // SaveScore(level_nr);
3735 static void InitMovingField_MM(int x, int y, int direction)
3737 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3738 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3740 MovDir[x][y] = direction;
3741 MovDir[newx][newy] = direction;
3743 if (Feld[newx][newy] == EL_EMPTY)
3744 Feld[newx][newy] = EL_BLOCKED;
3747 static void Moving2Blocked_MM(int x, int y, int *goes_to_x, int *goes_to_y)
3749 int direction = MovDir[x][y];
3750 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3751 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3757 static void Blocked2Moving_MM(int x, int y,
3758 int *comes_from_x, int *comes_from_y)
3760 int oldx = x, oldy = y;
3761 int direction = MovDir[x][y];
3763 if (direction == MV_LEFT)
3765 else if (direction == MV_RIGHT)
3767 else if (direction == MV_UP)
3769 else if (direction == MV_DOWN)
3772 *comes_from_x = oldx;
3773 *comes_from_y = oldy;
3776 static int MovingOrBlocked2Element_MM(int x, int y)
3778 int element = Feld[x][y];
3780 if (element == EL_BLOCKED)
3784 Blocked2Moving_MM(x, y, &oldx, &oldy);
3786 return Feld[oldx][oldy];
3793 static void RemoveField(int x, int y)
3795 Feld[x][y] = EL_EMPTY;
3802 static void RemoveMovingField_MM(int x, int y)
3804 int oldx = x, oldy = y, newx = x, newy = y;
3806 if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
3809 if (IS_MOVING(x, y))
3811 Moving2Blocked_MM(x, y, &newx, &newy);
3812 if (Feld[newx][newy] != EL_BLOCKED)
3815 else if (Feld[x][y] == EL_BLOCKED)
3817 Blocked2Moving_MM(x, y, &oldx, &oldy);
3818 if (!IS_MOVING(oldx, oldy))
3822 Feld[oldx][oldy] = EL_EMPTY;
3823 Feld[newx][newy] = EL_EMPTY;
3824 MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
3825 MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
3827 DrawLevelField_MM(oldx, oldy);
3828 DrawLevelField_MM(newx, newy);
3831 void PlaySoundLevel(int x, int y, int sound_nr)
3833 int sx = SCREENX(x), sy = SCREENY(y);
3835 int silence_distance = 8;
3837 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
3838 (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
3841 if (!IN_LEV_FIELD(x, y) ||
3842 sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
3843 sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
3846 volume = SOUND_MAX_VOLUME;
3849 stereo = (sx - SCR_FIELDX/2) * 12;
3851 stereo = SOUND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
3852 if (stereo > SOUND_MAX_RIGHT)
3853 stereo = SOUND_MAX_RIGHT;
3854 if (stereo < SOUND_MAX_LEFT)
3855 stereo = SOUND_MAX_LEFT;
3858 if (!IN_SCR_FIELD(sx, sy))
3860 int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
3861 int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
3863 volume -= volume * (dx > dy ? dx : dy) / silence_distance;
3866 PlaySoundExt(sound_nr, volume, stereo, SND_CTRL_PLAY_SOUND);
3869 static void RaiseScore_MM(int value)
3871 game_mm.score += value;
3874 DrawText(DX_SCORE, DY_SCORE, int2str(game_mm.score, 4),
3879 void RaiseScoreElement_MM(int element)
3884 RaiseScore_MM(native_mm_level.score[SC_PACMAN]);
3888 RaiseScore_MM(native_mm_level.score[SC_KEY]);