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 game_mm.game_over = TRUE;
2192 game_mm.game_over_cause = GAME_OVER_BOMB;
2194 laser.overloaded = FALSE;
2196 else if (IS_MCDUFFIN(Store[x][y]))
2198 Store[x][y] = EL_EMPTY;
2201 Feld[x][y] = Store[x][y];
2202 Store[x][y] = Store2[x][y] = 0;
2203 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2205 InitField(x, y, FALSE);
2208 else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2210 int graphic = IMG_MM_DEFAULT_EXPLODING;
2211 int graphic_phase = (phase / delay - 1);
2215 if (Store2[x][y] == EX_KETTLE)
2217 if (graphic_phase < 3)
2219 graphic = IMG_MM_KETTLE_EXPLODING;
2221 else if (graphic_phase < 5)
2227 graphic = IMG_EMPTY;
2231 else if (Store2[x][y] == EX_SHORT)
2233 if (graphic_phase < 4)
2239 graphic = IMG_EMPTY;
2244 getGraphicSource(graphic, graphic_phase, &bitmap, &src_x, &src_y);
2246 BlitBitmap(bitmap, drawto_field, src_x, src_y, TILEX, TILEY,
2247 FX + x * TILEX, FY + y * TILEY);
2249 MarkTileDirty(x, y);
2253 static void Bang_MM(int x, int y)
2255 int element = Feld[x][y];
2256 int mode = EX_NORMAL;
2259 DrawLaser(0, DL_LASER_ENABLED);
2278 if (IS_PACMAN(element))
2279 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2280 else if (element == EL_BOMB || IS_MCDUFFIN(element))
2281 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2282 else if (element == EL_KEY)
2283 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2285 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2287 Explode_MM(x, y, EX_PHASE_START, mode);
2290 void TurnRound(int x, int y)
2302 { 0, 0 }, { 0, 0 }, { 0, 0 },
2307 int left, right, back;
2311 { MV_DOWN, MV_UP, MV_RIGHT },
2312 { MV_UP, MV_DOWN, MV_LEFT },
2314 { MV_LEFT, MV_RIGHT, MV_DOWN },
2315 { 0,0,0 }, { 0,0,0 }, { 0,0,0 },
2316 { MV_RIGHT, MV_LEFT, MV_UP }
2319 int element = Feld[x][y];
2320 int old_move_dir = MovDir[x][y];
2321 int right_dir = turn[old_move_dir].right;
2322 int back_dir = turn[old_move_dir].back;
2323 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
2324 int right_x = x + right_dx, right_y = y + right_dy;
2326 if (element == EL_PACMAN)
2328 boolean can_turn_right = FALSE;
2330 if (IN_LEV_FIELD(right_x, right_y) &&
2331 IS_EATABLE4PACMAN(Feld[right_x][right_y]))
2332 can_turn_right = TRUE;
2335 MovDir[x][y] = right_dir;
2337 MovDir[x][y] = back_dir;
2343 static void StartMoving_MM(int x, int y)
2345 int element = Feld[x][y];
2350 if (CAN_MOVE(element))
2354 if (MovDelay[x][y]) /* wait some time before next movement */
2362 /* now make next step */
2364 Moving2Blocked_MM(x, y, &newx, &newy); /* get next screen position */
2366 if (element == EL_PACMAN &&
2367 IN_LEV_FIELD(newx, newy) && IS_EATABLE4PACMAN(Feld[newx][newy]) &&
2368 !ObjHit(newx, newy, HIT_POS_CENTER))
2370 Store[newx][newy] = Feld[newx][newy];
2371 Feld[newx][newy] = EL_EMPTY;
2373 DrawField_MM(newx, newy);
2375 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy) ||
2376 ObjHit(newx, newy, HIT_POS_CENTER))
2378 /* object was running against a wall */
2385 InitMovingField_MM(x, y, MovDir[x][y]);
2389 ContinueMoving_MM(x, y);
2392 static void ContinueMoving_MM(int x, int y)
2394 int element = Feld[x][y];
2395 int direction = MovDir[x][y];
2396 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2397 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2398 int horiz_move = (dx!=0);
2399 int newx = x + dx, newy = y + dy;
2400 int step = (horiz_move ? dx : dy) * TILEX / 8;
2402 MovPos[x][y] += step;
2404 if (ABS(MovPos[x][y]) >= TILEX) /* object reached its destination */
2406 Feld[x][y] = EL_EMPTY;
2407 Feld[newx][newy] = element;
2409 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2410 MovDelay[newx][newy] = 0;
2412 if (!CAN_MOVE(element))
2413 MovDir[newx][newy] = 0;
2416 DrawField_MM(newx, newy);
2418 Stop[newx][newy] = TRUE;
2420 if (element == EL_PACMAN)
2422 if (Store[newx][newy] == EL_BOMB)
2423 Bang_MM(newx, newy);
2425 if (IS_WALL_AMOEBA(Store[newx][newy]) &&
2426 (LX + 2 * XS) / TILEX == newx &&
2427 (LY + 2 * YS) / TILEY == newy)
2434 else /* still moving on */
2439 laser.redraw = TRUE;
2442 void ClickElement(int x, int y, int button)
2444 static unsigned int click_delay = 0;
2445 static int click_delay_value = CLICK_DELAY;
2446 static boolean new_button = TRUE;
2449 /* do not rotate objects hit by the laser after the game was solved */
2450 if (game_mm.level_solved && Hit[x][y])
2453 if (button == MB_RELEASED)
2456 click_delay_value = CLICK_DELAY;
2458 /* release eventually hold auto-rotating mirror */
2459 RotateMirror(x, y, MB_RELEASED);
2464 if (!FrameReached(&click_delay, click_delay_value) && !new_button)
2467 if (button == MB_MIDDLEBUTTON) /* middle button has no function */
2470 if (!IN_LEV_FIELD(x, y))
2473 if (Feld[x][y] == EL_EMPTY)
2476 element = Feld[x][y];
2478 if (IS_MIRROR(element) ||
2479 IS_BEAMER(element) ||
2480 IS_POLAR(element) ||
2481 IS_POLAR_CROSS(element) ||
2482 IS_DF_MIRROR(element) ||
2483 IS_DF_MIRROR_AUTO(element))
2485 RotateMirror(x, y, button);
2487 else if (IS_MCDUFFIN(element))
2489 if (!laser.fuse_off)
2491 DrawLaser(0, DL_LASER_DISABLED);
2498 element = get_rotated_element(element, BUTTON_ROTATION(button));
2499 laser.start_angle = get_element_angle(element);
2503 Feld[x][y] = element;
2510 if (!laser.fuse_off)
2513 else if (element == EL_FUSE_ON && laser.fuse_off)
2515 if (x != laser.fuse_x || y != laser.fuse_y)
2518 laser.fuse_off = FALSE;
2519 laser.fuse_x = laser.fuse_y = -1;
2521 DrawGraphic_MM(x, y, IMG_MM_FUSE_ACTIVE);
2524 else if (element == EL_FUSE_ON && !laser.fuse_off && new_button)
2526 laser.fuse_off = TRUE;
2529 laser.overloaded = FALSE;
2531 DrawLaser(0, DL_LASER_DISABLED);
2532 DrawGraphic_MM(x, y, IMG_MM_FUSE);
2534 else if (element == EL_LIGHTBALL)
2538 DrawLaser(0, DL_LASER_ENABLED);
2541 click_delay_value = (new_button ? CLICK_DELAY_FIRST : CLICK_DELAY);
2545 void RotateMirror(int x, int y, int button)
2547 static int hold_x = -1, hold_y = -1;
2549 if (button == MB_RELEASED)
2551 /* release eventually hold auto-rotating mirror */
2558 if (IS_MIRROR(Feld[x][y]) ||
2559 IS_POLAR_CROSS(Feld[x][y]) ||
2560 IS_POLAR(Feld[x][y]) ||
2561 IS_BEAMER(Feld[x][y]) ||
2562 IS_DF_MIRROR(Feld[x][y]) ||
2563 IS_GRID_STEEL_AUTO(Feld[x][y]) ||
2564 IS_GRID_WOOD_AUTO(Feld[x][y]))
2566 Feld[x][y] = get_rotated_element(Feld[x][y], BUTTON_ROTATION(button));
2568 else if (IS_DF_MIRROR_AUTO(Feld[x][y]))
2570 if (button == MB_LEFTBUTTON)
2572 /* left mouse button only for manual adjustment, no auto-rotating;
2573 freeze mirror for until mouse button released */
2577 else if (button == MB_RIGHTBUTTON && (hold_x != x || hold_y != y))
2579 Feld[x][y] = get_rotated_element(Feld[x][y], ROTATE_RIGHT);
2583 if (IS_GRID_STEEL_AUTO(Feld[x][y]) || IS_GRID_WOOD_AUTO(Feld[x][y]))
2585 int edge = Hit[x][y];
2591 DrawLaser(edge - 1, DL_LASER_DISABLED);
2595 else if (ObjHit(x, y, HIT_POS_CENTER))
2597 int edge = Hit[x][y];
2601 Error(ERR_WARN, "RotateMirror: inconsistent field Hit[][]!\n");
2605 DrawLaser(edge - 1, DL_LASER_DISABLED);
2612 if (ObjHit(x, y, HIT_POS_EDGE | HIT_POS_BETWEEN))
2617 if ((IS_BEAMER(Feld[x][y]) ||
2618 IS_POLAR(Feld[x][y]) ||
2619 IS_POLAR_CROSS(Feld[x][y])) && x == ELX && y == ELY)
2623 if (IS_BEAMER(Feld[x][y]))
2626 printf("TEST (%d, %d) [%d] [%d]\n",
2628 laser.beamer_edge, laser.beamer[1].num);
2638 DrawLaser(0, DL_LASER_ENABLED);
2642 void AutoRotateMirrors()
2644 static unsigned int rotate_delay = 0;
2647 if (!FrameReached(&rotate_delay, AUTO_ROTATE_DELAY))
2650 for (x = 0; x < lev_fieldx; x++)
2652 for (y = 0; y < lev_fieldy; y++)
2654 int element = Feld[x][y];
2656 /* do not rotate objects hit by the laser after the game was solved */
2657 if (game_mm.level_solved && Hit[x][y])
2660 if (IS_DF_MIRROR_AUTO(element) ||
2661 IS_GRID_WOOD_AUTO(element) ||
2662 IS_GRID_STEEL_AUTO(element) ||
2663 element == EL_REFRACTOR)
2664 RotateMirror(x, y, MB_RIGHTBUTTON);
2669 boolean ObjHit(int obx, int oby, int bits)
2676 if (bits & HIT_POS_CENTER)
2678 if (ReadPixel(drawto, SX + obx + 15, SY + oby + 15) == pen_ray)
2682 if (bits & HIT_POS_EDGE)
2684 for (i = 0; i < 4; i++)
2685 if (ReadPixel(drawto,
2686 SX + obx + 31 * (i % 2),
2687 SY + oby + 31 * (i / 2)) == pen_ray)
2691 if (bits & HIT_POS_BETWEEN)
2693 for (i = 0; i < 4; i++)
2694 if (ReadPixel(drawto,
2695 SX + 4 + obx + 22 * (i % 2),
2696 SY + 4 + oby + 22 * (i / 2)) == pen_ray)
2703 void DeletePacMan(int px, int py)
2709 if (game_mm.num_pacman <= 1)
2711 game_mm.num_pacman = 0;
2715 for (i = 0; i < game_mm.num_pacman; i++)
2716 if (game_mm.pacman[i].x == px && game_mm.pacman[i].y == py)
2719 game_mm.num_pacman--;
2721 for (j = i; j < game_mm.num_pacman; j++)
2723 game_mm.pacman[j].x = game_mm.pacman[j + 1].x;
2724 game_mm.pacman[j].y = game_mm.pacman[j + 1].y;
2725 game_mm.pacman[j].dir = game_mm.pacman[j + 1].dir;
2729 void ColorCycling(void)
2731 static int CC, Cc = 0;
2733 static int color, old = 0xF00, new = 0x010, mult = 1;
2734 static unsigned short red, green, blue;
2736 if (color_status == STATIC_COLORS)
2741 if (CC < Cc || CC > Cc + 50)
2745 color = old + new * mult;
2751 if (ABS(mult) == 16)
2761 red = 0x0e00 * ((color & 0xF00) >> 8);
2762 green = 0x0e00 * ((color & 0x0F0) >> 4);
2763 blue = 0x0e00 * ((color & 0x00F));
2764 SetRGB(pen_magicolor[0], red, green, blue);
2766 red = 0x1111 * ((color & 0xF00) >> 8);
2767 green = 0x1111 * ((color & 0x0F0) >> 4);
2768 blue = 0x1111 * ((color & 0x00F));
2769 SetRGB(pen_magicolor[1], red, green, blue);
2773 static void GameActions_MM_Ext(struct MouseActionInfo action, boolean warp_mode)
2775 static unsigned int pacman_delay = 0;
2776 static unsigned int energy_delay = 0;
2777 static unsigned int overload_delay = 0;
2783 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2786 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2788 element = Feld[x][y];
2790 if (!IS_MOVING(x, y) && CAN_MOVE(element))
2791 StartMoving_MM(x, y);
2792 else if (IS_MOVING(x, y))
2793 ContinueMoving_MM(x, y);
2794 else if (IS_EXPLODING(element))
2795 Explode_MM(x, y, Frame[x][y], EX_NORMAL);
2796 else if (element == EL_EXIT_OPENING)
2798 else if (element == EL_GRAY_BALL_OPENING)
2799 OpenSurpriseBall(x, y);
2800 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_ICE)
2802 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_AMOEBA)
2806 AutoRotateMirrors();
2809 /* !!! CHANGE THIS: REDRAW ONLY WHEN NEEDED !!! */
2811 /* redraw after Explode_MM() ... */
2813 DrawLaser(0, DL_LASER_ENABLED);
2814 laser.redraw = FALSE;
2819 if (game_mm.num_pacman && FrameReached(&pacman_delay, PACMAN_MOVE_DELAY))
2823 if (laser.num_damages > MAX_LASER_LEN && !laser.fuse_off)
2825 DrawLaser(0, DL_LASER_DISABLED);
2830 if (FrameReached(&energy_delay, ENERGY_DELAY))
2832 game_mm.energy_left--;
2833 if (game_mm.energy_left >= 0)
2836 BlitBitmap(pix[PIX_DOOR], drawto,
2837 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
2838 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
2839 DX_ENERGY, DY_ENERGY);
2841 redraw_mask |= REDRAW_DOOR_1;
2843 else if (setup.time_limit)
2847 for (i = 15; i >= 0; i--)
2850 SetRGB(pen_ray, 0x0000, 0x0000, i * color_scale);
2852 pen_ray = GetPixelFromRGB(window,
2853 native_mm_level.laser_red * 0x11 * i,
2854 native_mm_level.laser_green * 0x11 * i,
2855 native_mm_level.laser_blue * 0x11 * i);
2857 DrawLaser(0, DL_LASER_ENABLED);
2862 StopSound_MM(SND_MM_GAME_HEALTH_CHARGING);
2865 DrawLaser(0, DL_LASER_DISABLED);
2866 game_mm.game_over = TRUE;
2867 game_mm.game_over_cause = GAME_OVER_NO_ENERGY;
2870 if (Request("Out of magic energy ! Play it again ?",
2871 REQ_ASK | REQ_STAY_CLOSED))
2877 game_status = MAINMENU;
2886 element = laser.dest_element;
2889 if (element != Feld[ELX][ELY])
2891 printf("element == %d, Feld[ELX][ELY] == %d\n",
2892 element, Feld[ELX][ELY]);
2896 if (!laser.overloaded && laser.overload_value == 0 &&
2897 element != EL_BOMB &&
2898 element != EL_MINE &&
2899 element != EL_BALL_GRAY &&
2900 element != EL_BLOCK_STONE &&
2901 element != EL_BLOCK_WOOD &&
2902 element != EL_FUSE_ON &&
2903 element != EL_FUEL_FULL &&
2904 !IS_WALL_ICE(element) &&
2905 !IS_WALL_AMOEBA(element))
2908 if (((laser.overloaded && laser.overload_value < MAX_LASER_OVERLOAD) ||
2909 (!laser.overloaded && laser.overload_value > 0)) &&
2910 FrameReached(&overload_delay, HEALTH_DELAY(laser.overloaded)))
2912 if (laser.overloaded)
2913 laser.overload_value++;
2915 laser.overload_value--;
2917 if (game_mm.cheat_no_overload)
2919 laser.overloaded = FALSE;
2920 laser.overload_value = 0;
2923 game_mm.laser_overload_value = laser.overload_value;
2925 if (laser.overload_value < MAX_LASER_OVERLOAD - 8)
2927 int color_up = 0xFF * laser.overload_value / MAX_LASER_OVERLOAD;
2928 int color_down = 0xFF - color_up;
2931 SetRGB(pen_ray, (laser.overload_value / 6) * color_scale, 0x0000,
2932 (15 - (laser.overload_value / 6)) * color_scale);
2935 GetPixelFromRGB(window,
2936 (native_mm_level.laser_red ? 0xFF : color_up),
2937 (native_mm_level.laser_green ? color_down : 0x00),
2938 (native_mm_level.laser_blue ? color_down : 0x00));
2940 DrawLaser(0, DL_LASER_ENABLED);
2946 if (!laser.overloaded)
2947 StopSound_MM(SND_MM_GAME_HEALTH_CHARGING);
2948 else if (setup.sound_loops)
2949 PlaySoundLoop_MM(SND_MM_GAME_HEALTH_CHARGING);
2951 PlaySound_MM(SND_MM_GAME_HEALTH_CHARGING);
2953 if (laser.overloaded)
2956 BlitBitmap(pix[PIX_DOOR], drawto,
2957 DOOR_GFX_PAGEX4 + XX_OVERLOAD,
2958 DOOR_GFX_PAGEY1 + YY_OVERLOAD + OVERLOAD_YSIZE
2959 - laser.overload_value,
2960 OVERLOAD_XSIZE, laser.overload_value,
2961 DX_OVERLOAD, DY_OVERLOAD + OVERLOAD_YSIZE
2962 - laser.overload_value);
2964 redraw_mask |= REDRAW_DOOR_1;
2969 BlitBitmap(pix[PIX_DOOR], drawto,
2970 DOOR_GFX_PAGEX5 + XX_OVERLOAD, DOOR_GFX_PAGEY1 + YY_OVERLOAD,
2971 OVERLOAD_XSIZE, OVERLOAD_YSIZE - laser.overload_value,
2972 DX_OVERLOAD, DY_OVERLOAD);
2974 redraw_mask |= REDRAW_DOOR_1;
2977 if (laser.overload_value == MAX_LASER_OVERLOAD)
2981 for (i = 15; i >= 0; i--)
2984 SetRGB(pen_ray, i * color_scale, 0x0000, 0x0000);
2987 pen_ray = GetPixelFromRGB(window, 0x11 * i, 0x00, 0x00);
2989 DrawLaser(0, DL_LASER_ENABLED);
2994 DrawLaser(0, DL_LASER_DISABLED);
2996 game_mm.game_over = TRUE;
2997 game_mm.game_over_cause = GAME_OVER_OVERLOADED;
3000 if (Request("Magic spell hit Mc Duffin ! Play it again ?",
3001 REQ_ASK | REQ_STAY_CLOSED))
3007 game_status = MAINMENU;
3021 if (element == EL_BOMB && CT > 1500)
3023 if (game_mm.cheat_no_explosion)
3027 laser.num_damages--;
3028 DrawLaser(0, DL_LASER_DISABLED);
3029 laser.num_edges = 0;
3034 laser.dest_element = EL_EXPLODING_OPAQUE;
3038 laser.num_damages--;
3039 DrawLaser(0, DL_LASER_DISABLED);
3041 laser.num_edges = 0;
3042 Bang_MM(laser.start_edge.x, laser.start_edge.y);
3044 if (Request("Bomb killed Mc Duffin ! Play it again ?",
3045 REQ_ASK | REQ_STAY_CLOSED))
3051 game_status = MAINMENU;
3059 if (element == EL_FUSE_ON && CT > 500)
3061 laser.fuse_off = TRUE;
3065 DrawLaser(0, DL_LASER_DISABLED);
3066 DrawGraphic_MM(ELX, ELY, IMG_MM_FUSE);
3069 if (element == EL_BALL_GRAY && CT > 1500)
3071 static int new_elements[] =
3074 EL_MIRROR_FIXED_START,
3076 EL_POLAR_CROSS_START,
3082 int num_new_elements = sizeof(new_elements) / sizeof(int);
3083 int new_element = new_elements[RND(num_new_elements)];
3085 Store[ELX][ELY] = new_element + RND(get_num_elements(new_element));
3086 Feld[ELX][ELY] = EL_GRAY_BALL_OPENING;
3088 /* !!! CHECK AGAIN: Laser on Polarizer !!! */
3099 element = EL_MIRROR_START + RND(16);
3105 element = (rnd == 0 ? EL_KETTLE : rnd == 1 ? EL_BOMB : EL_PRISM);
3112 element = (rnd == 0 ? EL_FUSE_ON :
3113 rnd >= 1 && rnd <= 4 ? EL_PACMAN_RIGHT + rnd - 1 :
3114 rnd >= 5 && rnd <= 20 ? EL_POLAR_START + rnd - 5 :
3115 rnd >= 21 && rnd <= 24 ? EL_POLAR_CROSS_START + rnd - 21 :
3116 EL_MIRROR_FIXED_START + rnd - 25);
3121 graphic = el2gfx(element);
3123 for (i = 0; i < 50; i++)
3129 BlitBitmap(pix[PIX_BACK], drawto,
3130 SX + (graphic % GFX_PER_LINE) * TILEX + x,
3131 SY + (graphic / GFX_PER_LINE) * TILEY + y, 6, 6,
3132 SX + ELX * TILEX + x,
3133 SY + ELY * TILEY + y);
3135 MarkTileDirty(ELX, ELY);
3138 DrawLaser(0, DL_LASER_ENABLED);
3143 Feld[ELX][ELY] = element;
3144 DrawField_MM(ELX, ELY);
3147 printf("NEW ELEMENT: (%d, %d)\n", ELX, ELY);
3150 /* above stuff: GRAY BALL -> PRISM !!! */
3152 LX = ELX * TILEX + 14;
3153 LY = ELY * TILEY + 14;
3154 if (laser.current_angle == (laser.current_angle >> 1) << 1)
3161 laser.num_edges -= 2;
3162 laser.num_damages--;
3166 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
3167 if (laser.damage[i].is_mirror)
3171 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3173 DrawLaser(0, DL_LASER_DISABLED);
3175 DrawLaser(0, DL_LASER_DISABLED);
3181 printf("TEST ELEMENT: %d\n", Feld[0][0]);
3188 if (IS_WALL_ICE(element) && CT > 1000)
3190 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_SHRINKING);
3193 Feld[ELX][ELY] = Feld[ELX][ELY] - EL_WALL_ICE + EL_WALL_CHANGING;
3194 Store[ELX][ELY] = EL_WALL_ICE;
3195 Store2[ELX][ELY] = laser.wall_mask;
3197 laser.dest_element = Feld[ELX][ELY];
3202 for (i = 0; i < 5; i++)
3208 Feld[ELX][ELY] &= (laser.wall_mask ^ 0xFF);
3212 DrawWallsAnimation_MM(ELX, ELY, Feld[ELX][ELY], phase, laser.wall_mask);
3217 if (Feld[ELX][ELY] == EL_WALL_ICE)
3218 Feld[ELX][ELY] = EL_EMPTY;
3222 LX = laser.edge[laser.num_edges].x - (SX + 2);
3223 LY = laser.edge[laser.num_edges].y - (SY + 2);
3226 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
3227 if (laser.damage[i].is_mirror)
3231 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3233 DrawLaser(0, DL_LASER_DISABLED);
3240 if (IS_WALL_AMOEBA(element) && CT > 1200)
3242 int k1, k2, k3, dx, dy, de, dm;
3243 int element2 = Feld[ELX][ELY];
3245 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element))
3248 for (i = laser.num_damages - 1; i >= 0; i--)
3249 if (laser.damage[i].is_mirror)
3252 r = laser.num_edges;
3253 d = laser.num_damages;
3260 DrawLaser(laser.damage[k1].edge - 1, DL_LASER_DISABLED);
3263 DrawLaser(0, DL_LASER_ENABLED);
3266 x = laser.damage[k1].x;
3267 y = laser.damage[k1].y;
3272 for (i = 0; i < 4; i++)
3274 if (laser.wall_mask & (1 << i))
3276 if (ReadPixel(drawto,
3277 SX + ELX * TILEX + 14 + (i % 2) * 2,
3278 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3280 if (ReadPixel(drawto,
3281 SX + ELX * TILEX + 31 * (i % 2),
3282 SY + ELY * TILEY + 14 + (i / 2) * 2) == pen_ray)
3289 for (i = 0; i < 4; i++)
3291 if (laser.wall_mask & (1 << i))
3293 if (ReadPixel(drawto,
3294 SX + ELX * TILEX + 31 * (i % 2),
3295 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3302 if (laser.num_beamers > 0 ||
3303 k1 < 1 || k2 < 4 || k3 < 4 ||
3304 ReadPixel(drawto, SX + ELX * TILEX + 14, SY + ELY * TILEY + 14)
3307 laser.num_edges = r;
3308 laser.num_damages = d;
3310 DrawLaser(0, DL_LASER_DISABLED);
3313 Feld[ELX][ELY] = element | laser.wall_mask;
3317 de = Feld[ELX][ELY];
3318 dm = laser.wall_mask;
3322 int x = ELX, y = ELY;
3323 int wall_mask = laser.wall_mask;
3326 DrawLaser(0, DL_LASER_ENABLED);
3328 PlayLevelSound_MM(dx, dy, element, MM_ACTION_GROWING);
3330 Feld[x][y] = Feld[x][y] - EL_WALL_AMOEBA + EL_WALL_CHANGING;
3331 Store[x][y] = EL_WALL_AMOEBA;
3332 Store2[x][y] = wall_mask;
3338 DrawWallsAnimation_MM(dx, dy, de, 4, dm);
3340 DrawLaser(0, DL_LASER_ENABLED);
3342 PlayLevelSound_MM(dx, dy, element, MM_ACTION_GROWING);
3344 for (i = 4; i >= 0; i--)
3346 DrawWallsAnimation_MM(dx, dy, de, i, dm);
3352 DrawLaser(0, DL_LASER_ENABLED);
3357 if ((element == EL_BLOCK_WOOD || element == EL_BLOCK_STONE) &&
3358 laser.stops_inside_element && CT > 1500)
3363 if (ABS(XS) > ABS(YS))
3370 for (i = 0; i < 4; i++)
3377 x = ELX + Step[k * 4].x;
3378 y = ELY + Step[k * 4].y;
3380 if (!IN_LEV_FIELD(x, y) || Feld[x][y] != EL_EMPTY)
3383 if (ObjHit(x, y, HIT_POS_CENTER | HIT_POS_EDGE | HIT_POS_BETWEEN))
3391 laser.overloaded = (element == EL_BLOCK_STONE);
3396 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_PUSHING);
3399 Feld[x][y] = element;
3401 DrawGraphic_MM(ELX, ELY, IMG_EMPTY);
3404 if (element == EL_BLOCK_STONE && Box[ELX][ELY])
3406 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
3407 DrawLaser(laser.num_edges - 1, DL_LASER_ENABLED);
3415 if (element == EL_FUEL_FULL && CT > 200)
3417 for (i = game_mm.energy_left; i <= MAX_LASER_ENERGY; i+=2)
3420 BlitBitmap(pix[PIX_DOOR], drawto,
3421 DOOR_GFX_PAGEX4 + XX_ENERGY,
3422 DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
3423 ENERGY_XSIZE, i, DX_ENERGY,
3424 DY_ENERGY + ENERGY_YSIZE - i);
3427 redraw_mask |= REDRAW_DOOR_1;
3433 game_mm.energy_left = MAX_LASER_ENERGY;
3434 Feld[ELX][ELY] = EL_FUEL_EMPTY;
3435 DrawField_MM(ELX, ELY);
3437 DrawLaser(0, DL_LASER_ENABLED);
3445 void GameActions_MM(struct MouseActionInfo action, boolean warp_mode)
3447 ClickElement(action.lx, action.ly, action.button);
3449 GameActions_MM_Ext(action, warp_mode);
3455 int mx, my, ox, oy, nx, ny;
3459 if (++p >= game_mm.num_pacman)
3462 game_mm.pacman[p].dir--;
3464 for (l = 1; l < 5; l++)
3466 game_mm.pacman[p].dir++;
3468 if (game_mm.pacman[p].dir > 4)
3469 game_mm.pacman[p].dir = 1;
3471 if (game_mm.pacman[p].dir % 2)
3474 my = game_mm.pacman[p].dir - 2;
3479 mx = 3 - game_mm.pacman[p].dir;
3482 ox = game_mm.pacman[p].x;
3483 oy = game_mm.pacman[p].y;
3486 element = Feld[nx][ny];
3488 if (nx < 0 || nx > 15 || ny < 0 || ny > 11)
3491 if (!IS_EATABLE4PACMAN(element))
3494 if (ObjHit(nx, ny, HIT_POS_CENTER))
3497 Feld[ox][oy] = EL_EMPTY;
3499 EL_PACMAN_RIGHT - 1 +
3500 (game_mm.pacman[p].dir - 1 +
3501 (game_mm.pacman[p].dir % 2) * 2);
3503 game_mm.pacman[p].x = nx;
3504 game_mm.pacman[p].y = ny;
3506 DrawGraphic_MM(ox, oy, IMG_EMPTY);
3508 if (element != EL_EMPTY)
3510 int graphic = el2gfx(Feld[nx][ny]);
3515 getGraphicSource(graphic, 0, &bitmap, &src_x, &src_y);
3518 ox = SX + ox * TILEX;
3519 oy = SY + oy * TILEY;
3521 for (i = 1; i < 33; i += 2)
3522 BlitBitmap(bitmap, window,
3523 src_x, src_y, TILEX, TILEY,
3524 ox + i * mx, oy + i * my);
3525 Ct = Ct + Counter() - CT;
3528 DrawField_MM(nx, ny);
3531 if (!laser.fuse_off)
3533 DrawLaser(0, DL_LASER_ENABLED);
3535 if (ObjHit(nx, ny, HIT_POS_BETWEEN))
3537 AddDamagedField(nx, ny);
3539 laser.damage[laser.num_damages - 1].edge = 0;
3543 if (element == EL_BOMB)
3544 DeletePacMan(nx, ny);
3546 if (IS_WALL_AMOEBA(element) &&
3547 (LX + 2 * XS) / TILEX == nx &&
3548 (LY + 2 * YS) / TILEY == ny)
3561 boolean raise_level = FALSE;
3564 if (local_player->MovPos)
3567 local_player->LevelSolved = FALSE;
3570 if (game_mm.energy_left)
3572 if (setup.sound_loops)
3573 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3574 SND_CTRL_PLAY_LOOP);
3576 while (game_mm.energy_left > 0)
3578 if (!setup.sound_loops)
3579 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3582 if (game_mm.energy_left > 0 && !(game_mm.energy_left % 10))
3583 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3588 game_mm.energy_left--;
3589 if (game_mm.energy_left >= 0)
3592 BlitBitmap(pix[PIX_DOOR], drawto,
3593 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
3594 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
3595 DX_ENERGY, DY_ENERGY);
3597 redraw_mask |= REDRAW_DOOR_1;
3604 if (setup.sound_loops)
3605 StopSound(SND_SIRR);
3607 else if (native_mm_level.time == 0) /* level without time limit */
3609 if (setup.sound_loops)
3610 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3611 SND_CTRL_PLAY_LOOP);
3613 while (TimePlayed < 999)
3615 if (!setup.sound_loops)
3616 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3617 if (TimePlayed < 999 && !(TimePlayed % 10))
3618 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3619 if (TimePlayed < 900 && !(TimePlayed % 10))
3625 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
3632 if (setup.sound_loops)
3633 StopSound(SND_SIRR);
3640 CloseDoor(DOOR_CLOSE_1);
3642 Request("Level solved !", REQ_CONFIRM);
3644 if (level_nr == leveldir_current->handicap_level)
3646 leveldir_current->handicap_level++;
3647 SaveLevelSetup_SeriesInfo();
3650 if (level_editor_test_game)
3651 game_mm.score = -1; /* no highscore when playing from editor */
3652 else if (level_nr < leveldir_current->last_level)
3653 raise_level = TRUE; /* advance to next level */
3655 if ((hi_pos = NewHiScore_MM()) >= 0)
3657 game_status = HALLOFFAME;
3659 // DrawHallOfFame(hi_pos);
3666 game_status = MAINMENU;
3682 // LoadScore(level_nr);
3684 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
3685 game_mm.score < highscore[MAX_SCORE_ENTRIES - 1].Score)
3688 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3690 if (game_mm.score > highscore[k].Score)
3692 /* player has made it to the hall of fame */
3694 if (k < MAX_SCORE_ENTRIES - 1)
3696 int m = MAX_SCORE_ENTRIES - 1;
3699 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3700 if (!strcmp(setup.player_name, highscore[l].Name))
3702 if (m == k) /* player's new highscore overwrites his old one */
3706 for (l = m; l>k; l--)
3708 strcpy(highscore[l].Name, highscore[l - 1].Name);
3709 highscore[l].Score = highscore[l - 1].Score;
3716 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3717 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3718 highscore[k].Score = game_mm.score;
3725 else if (!strncmp(setup.player_name, highscore[k].Name,
3726 MAX_PLAYER_NAME_LEN))
3727 break; /* player already there with a higher score */
3732 // if (position >= 0)
3733 // SaveScore(level_nr);
3738 static void InitMovingField_MM(int x, int y, int direction)
3740 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3741 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3743 MovDir[x][y] = direction;
3744 MovDir[newx][newy] = direction;
3746 if (Feld[newx][newy] == EL_EMPTY)
3747 Feld[newx][newy] = EL_BLOCKED;
3750 static void Moving2Blocked_MM(int x, int y, int *goes_to_x, int *goes_to_y)
3752 int direction = MovDir[x][y];
3753 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3754 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3760 static void Blocked2Moving_MM(int x, int y,
3761 int *comes_from_x, int *comes_from_y)
3763 int oldx = x, oldy = y;
3764 int direction = MovDir[x][y];
3766 if (direction == MV_LEFT)
3768 else if (direction == MV_RIGHT)
3770 else if (direction == MV_UP)
3772 else if (direction == MV_DOWN)
3775 *comes_from_x = oldx;
3776 *comes_from_y = oldy;
3779 static int MovingOrBlocked2Element_MM(int x, int y)
3781 int element = Feld[x][y];
3783 if (element == EL_BLOCKED)
3787 Blocked2Moving_MM(x, y, &oldx, &oldy);
3789 return Feld[oldx][oldy];
3796 static void RemoveField(int x, int y)
3798 Feld[x][y] = EL_EMPTY;
3805 static void RemoveMovingField_MM(int x, int y)
3807 int oldx = x, oldy = y, newx = x, newy = y;
3809 if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
3812 if (IS_MOVING(x, y))
3814 Moving2Blocked_MM(x, y, &newx, &newy);
3815 if (Feld[newx][newy] != EL_BLOCKED)
3818 else if (Feld[x][y] == EL_BLOCKED)
3820 Blocked2Moving_MM(x, y, &oldx, &oldy);
3821 if (!IS_MOVING(oldx, oldy))
3825 Feld[oldx][oldy] = EL_EMPTY;
3826 Feld[newx][newy] = EL_EMPTY;
3827 MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
3828 MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
3830 DrawLevelField_MM(oldx, oldy);
3831 DrawLevelField_MM(newx, newy);
3834 void PlaySoundLevel(int x, int y, int sound_nr)
3836 int sx = SCREENX(x), sy = SCREENY(y);
3838 int silence_distance = 8;
3840 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
3841 (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
3844 if (!IN_LEV_FIELD(x, y) ||
3845 sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
3846 sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
3849 volume = SOUND_MAX_VOLUME;
3852 stereo = (sx - SCR_FIELDX/2) * 12;
3854 stereo = SOUND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
3855 if (stereo > SOUND_MAX_RIGHT)
3856 stereo = SOUND_MAX_RIGHT;
3857 if (stereo < SOUND_MAX_LEFT)
3858 stereo = SOUND_MAX_LEFT;
3861 if (!IN_SCR_FIELD(sx, sy))
3863 int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
3864 int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
3866 volume -= volume * (dx > dy ? dx : dy) / silence_distance;
3869 PlaySoundExt(sound_nr, volume, stereo, SND_CTRL_PLAY_SOUND);
3872 static void RaiseScore_MM(int value)
3874 game_mm.score += value;
3877 DrawText(DX_SCORE, DY_SCORE, int2str(game_mm.score, 4),
3882 void RaiseScoreElement_MM(int element)
3887 RaiseScore_MM(native_mm_level.score[SC_PACMAN]);
3891 RaiseScore_MM(native_mm_level.score[SC_KEY]);