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_SHORT 125
70 #define CLICK_DELAY_LONG 250
71 #define AUTO_ROTATE_DELAY CLICK_DELAY_SHORT
73 /* forward declaration for internal use */
74 static int MovingOrBlocked2Element_MM(int, int);
75 static void Bang_MM(int, int);
76 static void RaiseScore_MM(int);
77 static void RemoveMovingField_MM(int, int);
78 static void InitMovingField_MM(int, int, int);
79 static void ContinueMoving_MM(int, int);
80 static void Moving2Blocked_MM(int, int, int *, int *);
83 static int get_element_angle(int element)
85 int element_phase = get_element_phase(element);
87 if (IS_MIRROR_FIXED(element) ||
88 IS_MCDUFFIN(element) ||
91 return 4 * element_phase;
96 static int get_opposite_angle(int angle)
98 int opposite_angle = angle + ANG_RAY_180;
100 /* make sure "opposite_angle" is in valid interval [0, 15] */
101 return (opposite_angle + 16) % 16;
104 static int get_mirrored_angle(int laser_angle, int mirror_angle)
106 int reflected_angle = 16 - laser_angle + mirror_angle;
108 /* make sure "reflected_angle" is in valid interval [0, 15] */
109 return (reflected_angle + 16) % 16;
112 static void InitMovDir_MM(int x, int y)
114 int element = Feld[x][y];
115 static int direction[3][4] =
117 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
118 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
119 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
124 case EL_PACMAN_RIGHT:
128 Feld[x][y] = EL_PACMAN;
129 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
137 static void InitField(int x, int y, boolean init_game)
139 int element = Feld[x][y];
144 Feld[x][y] = EL_EMPTY;
149 if (native_mm_level.auto_count_kettles)
150 game_mm.kettles_still_needed++;
153 case EL_LIGHTBULB_OFF:
154 game_mm.lights_still_needed++;
158 if (IS_MIRROR(element) ||
159 IS_BEAMER_OLD(element) ||
160 IS_BEAMER(element) ||
162 IS_POLAR_CROSS(element) ||
163 IS_DF_MIRROR(element) ||
164 IS_DF_MIRROR_AUTO(element) ||
165 IS_GRID_STEEL_AUTO(element) ||
166 IS_GRID_WOOD_AUTO(element) ||
167 IS_FIBRE_OPTIC(element))
169 if (IS_BEAMER_OLD(element))
171 Feld[x][y] = EL_BEAMER_BLUE_START + (element - EL_BEAMER_START);
172 element = Feld[x][y];
175 if (!IS_FIBRE_OPTIC(element))
177 static int steps_grid_auto = 0;
179 if (game_mm.num_cycle == 0) /* initialize cycle steps for grids */
180 steps_grid_auto = RND(16) * (RND(2) ? -1 : +1);
182 if (IS_GRID_STEEL_AUTO(element) ||
183 IS_GRID_WOOD_AUTO(element))
184 game_mm.cycle[game_mm.num_cycle].steps = steps_grid_auto;
186 game_mm.cycle[game_mm.num_cycle].steps = RND(16) * (RND(2) ? -1 : +1);
188 game_mm.cycle[game_mm.num_cycle].x = x;
189 game_mm.cycle[game_mm.num_cycle].y = y;
193 if (IS_BEAMER(element) || IS_FIBRE_OPTIC(element))
195 int beamer_nr = BEAMER_NR(element);
196 int nr = laser.beamer[beamer_nr][0].num;
198 laser.beamer[beamer_nr][nr].x = x;
199 laser.beamer[beamer_nr][nr].y = y;
200 laser.beamer[beamer_nr][nr].num = 1;
203 else if (IS_PACMAN(element))
207 else if (IS_MCDUFFIN(element) || IS_LASER(element))
209 laser.start_edge.x = x;
210 laser.start_edge.y = y;
211 laser.start_angle = get_element_angle(element);
218 static void InitCycleElements()
222 if (game_mm.num_cycle == 0) /* no elements to cycle */
225 for (i = 0; i < 16; i++)
227 for (j = 0; j < game_mm.num_cycle; j++)
229 int x = game_mm.cycle[j].x;
230 int y = game_mm.cycle[j].y;
231 int step = SIGN(game_mm.cycle[j].steps);
232 int last_element = Feld[x][y];
233 int next_element = get_rotated_element(last_element, step);
235 if (!game_mm.cycle[j].steps)
238 Feld[x][y] = next_element;
241 game_mm.cycle[j].steps -= step;
248 if (setup.quick_doors)
252 Delay(AUTO_ROTATE_DELAY);
256 static void InitLaser()
258 int start_element = Feld[laser.start_edge.x][laser.start_edge.y];
259 int step = (IS_LASER(start_element) ? 4 : 0);
261 LX = laser.start_edge.x * TILEX;
262 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
265 LX += (laser.start_angle == ANG_RAY_RIGHT ? 28 + step : 0 - step);
267 LY = laser.start_edge.y * TILEY;
268 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
269 LY += (laser.start_angle == ANG_RAY_DOWN ? 28 + step : 0 - step);
273 XS = 2 * Step[laser.start_angle].x;
274 YS = 2 * Step[laser.start_angle].y;
276 laser.current_angle = laser.start_angle;
278 laser.num_damages = 0;
280 laser.num_beamers = 0;
281 laser.beamer_edge[0] = 0;
283 AddLaserEdge(LX, LY); /* set laser starting edge */
285 pen_ray = GetPixelFromRGB(window,
286 native_mm_level.laser_red * 0xFF,
287 native_mm_level.laser_green * 0xFF,
288 native_mm_level.laser_blue * 0xFF);
291 void InitGameEngine_MM()
295 /* set global game control values */
296 game_mm.num_cycle = 0;
297 game_mm.num_pacman = 0;
300 game_mm.energy_left = native_mm_level.time;
301 game_mm.kettles_still_needed =
302 (native_mm_level.auto_count_kettles ? 0 : native_mm_level.kettles_needed);
303 game_mm.lights_still_needed = 0;
304 game_mm.num_keys = 0;
306 game_mm.level_solved = FALSE;
307 game_mm.game_over = FALSE;
308 game_mm.game_over_cause = 0;
310 game_mm.laser_overload_value = 0;
312 /* set global laser control values (must be set before "InitLaser()") */
313 laser.start_edge.x = 0;
314 laser.start_edge.y = 0;
315 laser.start_angle = 0;
317 for (i = 0; i < MAX_NUM_BEAMERS; i++)
318 laser.beamer[i][0].num = laser.beamer[i][1].num = 0;
320 laser.overloaded = FALSE;
321 laser.overload_value = 0;
322 laser.fuse_off = FALSE;
323 laser.fuse_x = laser.fuse_y = -1;
325 laser.dest_element = EL_EMPTY;
330 for (x = 0; x < lev_fieldx; x++)
332 for (y = 0; y < lev_fieldy; y++)
334 Feld[x][y] = Ur[x][y];
335 Hit[x][y] = Box[x][y] = 0;
337 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
338 Store[x][y] = Store2[x][y] = 0;
342 InitField(x, y, TRUE);
347 CloseDoor(DOOR_CLOSE_1);
353 void InitGameEngine_MM_AfterFadingIn()
359 /* copy default game door content to main double buffer */
360 BlitBitmap(pix[PIX_DOOR], drawto,
361 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
365 DrawText(DX_LEVEL, DY_LEVEL,
366 int2str(level_nr, 2), FONT_TEXT_2);
367 DrawText(DX_KETTLES, DY_KETTLES,
368 int2str(game_mm.kettles_still_needed, 3), FONT_TEXT_2);
369 DrawText(DX_SCORE, DY_SCORE,
370 int2str(game_mm.score, 4), FONT_TEXT_2);
379 /* copy actual game door content to door double buffer for OpenDoor() */
380 BlitBitmap(drawto, pix[PIX_DB_DOOR],
381 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
385 OpenDoor(DOOR_OPEN_ALL);
388 if (setup.sound_loops)
389 PlaySoundExt(SND_FUEL, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT, SND_CTRL_PLAY_LOOP);
391 #if 0 // !!! TEMPORARILY DISABLED !!!
392 for (i = 0; i <= game_mm.energy_left; i += 2)
394 if (!setup.sound_loops)
395 PlaySoundStereo(SND_FUEL, SOUND_MAX_RIGHT);
398 BlitBitmap(pix[PIX_DOOR], drawto,
399 DOOR_GFX_PAGEX4 + XX_ENERGY,
400 DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
402 DX_ENERGY, DY_ENERGY + ENERGY_YSIZE - i);
405 redraw_mask |= REDRAW_DOOR_1;
411 if (setup.quick_doors)
418 if (setup.sound_loops)
423 if (setup.sound_music && num_bg_loops)
424 PlayMusic(level_nr % num_bg_loops);
430 void AddLaserEdge(int lx, int ly)
432 if (lx < -2 || ly < -2 || lx >= SXSIZE + 2 || ly >= SYSIZE + 2)
434 Error(ERR_WARN, "AddLaserEdge: out of bounds: %d, %d", lx, ly);
439 laser.edge[laser.num_edges].x = SX + 2 + lx;
440 laser.edge[laser.num_edges].y = SY + 2 + ly;
446 void AddDamagedField(int ex, int ey)
448 laser.damage[laser.num_damages].is_mirror = FALSE;
449 laser.damage[laser.num_damages].angle = laser.current_angle;
450 laser.damage[laser.num_damages].edge = laser.num_edges;
451 laser.damage[laser.num_damages].x = ex;
452 laser.damage[laser.num_damages].y = ey;
462 int last_x = laser.edge[laser.num_edges - 1].x - SX - 2;
463 int last_y = laser.edge[laser.num_edges - 1].y - SY - 2;
465 return ((x - last_x) * XS < 0 || (y - last_y) * YS < 0);
471 static int getMaskFromElement(int element)
473 if (IS_GRID(element))
474 return IMG_MM_MASK_GRID_1 + get_element_phase(element);
475 else if (IS_MCDUFFIN(element))
476 return IMG_MM_MASK_MCDUFFIN_RIGHT + get_element_phase(element);
477 else if (IS_RECTANGLE(element) || IS_DF_GRID(element))
478 return IMG_MM_MASK_RECTANGLE;
480 return IMG_MM_MASK_CIRCLE;
488 printf("ScanPixel: start scanning at (%d, %d) [%d, %d] [%d, %d]\n",
489 LX, LY, LX / TILEX, LY / TILEY, LX % TILEX, LY % TILEY);
492 /* follow laser beam until it hits something (at least the screen border) */
493 while (hit_mask == HIT_MASK_NO_HIT)
499 if (SX + LX < REAL_SX || SX + LX >= REAL_SX + FULL_SXSIZE ||
500 SY + LY < REAL_SY || SY + LY >= REAL_SY + FULL_SYSIZE)
502 printf("ScanPixel: touched screen border!\n");
508 for (i = 0; i < 4; i++)
510 int px = LX + (i % 2) * 2;
511 int py = LY + (i / 2) * 2;
514 int lx = (px + TILEX) / TILEX - 1; /* ...+TILEX...-1 to get correct */
515 int ly = (py + TILEY) / TILEY - 1; /* negative values! */
518 if (IN_LEV_FIELD(lx, ly))
520 int element = Feld[lx][ly];
522 if (element == EL_EMPTY || element == EL_EXPLODING_TRANSP)
526 else if (IS_WALL(element) || IS_WALL_CHANGING(element))
528 int pos = dy / MINI_TILEY * 2 + dx / MINI_TILEX;
530 pixel = ((element & (1 << pos)) ? 1 : 0);
534 int graphic_mask = getMaskFromElement(element);
539 getGraphicSource(graphic_mask, 0, &bitmap, &src_x, &src_y);
544 pixel = (ReadPixel(bitmap, mask_x, mask_y) ? 1 : 0);
549 pixel = (SX + px < REAL_SX || SX + px >= REAL_SX + FULL_SXSIZE ||
550 SY + py < REAL_SY || SY + py >= REAL_SY + FULL_SYSIZE);
553 if ((Sign[laser.current_angle] & (1 << i)) && pixel)
554 hit_mask |= (1 << i);
557 if (hit_mask == HIT_MASK_NO_HIT)
559 /* hit nothing -- go on with another step */
571 int end = 0, rf = laser.num_edges;
573 /* do not scan laser again after the game was lost for whatever reason */
574 if (game_mm.game_over)
577 laser.overloaded = FALSE;
578 laser.stops_inside_element = FALSE;
580 DrawLaser(0, DL_LASER_ENABLED);
583 printf("Start scanning with LX == %d, LY == %d, XS == %d, YS == %d\n",
591 if (laser.num_edges > MAX_LASER_LEN || laser.num_damages > MAX_LASER_LEN)
594 laser.overloaded = TRUE;
599 hit_mask = ScanPixel();
602 printf("Hit something at LX == %d, LY == %d, XS == %d, YS == %d\n",
606 /* hit something -- check out what it was */
607 ELX = (LX + XS) / TILEX;
608 ELY = (LY + YS) / TILEY;
611 printf("hit_mask (1) == '%x' (%d, %d) (%d, %d)\n",
612 hit_mask, LX, LY, ELX, ELY);
615 if (!IN_LEV_FIELD(ELX, ELY) || !IN_PIX_FIELD(LX, LY))
618 laser.dest_element = element;
623 if (hit_mask == (HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT))
625 /* we have hit the top-right and bottom-left element --
626 choose the bottom-left one */
627 /* !!! THIS CAN BE DONE MORE INTELLIGENTLY, FOR EXAMPLE, IF ONE
628 ELEMENT WAS STEEL AND THE OTHER ONE WAS ICE => ALWAYS CHOOSE
629 THE ICE AND MELT IT AWAY INSTEAD OF OVERLOADING LASER !!! */
630 ELX = (LX - 2) / TILEX;
631 ELY = (LY + 2) / TILEY;
634 if (hit_mask == (HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT))
636 /* we have hit the top-left and bottom-right element --
637 choose the top-left one */
638 /* !!! SEE ABOVE !!! */
639 ELX = (LX - 2) / TILEX;
640 ELY = (LY - 2) / TILEY;
644 printf("hit_mask (2) == '%x' (%d, %d) (%d, %d)\n",
645 hit_mask, LX, LY, ELX, ELY);
648 element = Feld[ELX][ELY];
649 laser.dest_element = element;
652 printf("Hit element %d at (%d, %d) [%d, %d] [%d, %d] [%d]\n",
655 LX % TILEX, LY % TILEY,
660 if (!IN_LEV_FIELD(ELX, ELY))
661 printf("WARNING! (1) %d, %d (%d)\n", ELX, ELY, element);
664 if (element == EL_EMPTY)
666 if (!HitOnlyAnEdge(element, hit_mask))
669 else if (element == EL_FUSE_ON)
671 if (HitPolarizer(element, hit_mask))
674 else if (IS_GRID(element) || IS_DF_GRID(element))
676 if (HitPolarizer(element, hit_mask))
679 else if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD ||
680 element == EL_GATE_STONE || element == EL_GATE_WOOD)
682 if (HitBlock(element, hit_mask))
689 else if (IS_MCDUFFIN(element))
691 if (HitLaserSource(element, hit_mask))
694 else if ((element >= EL_EXIT_CLOSED && element <= EL_EXIT_OPEN) ||
695 IS_RECEIVER(element))
697 if (HitLaserDestination(element, hit_mask))
700 else if (IS_WALL(element))
702 if (IS_WALL_STEEL(element) || IS_DF_WALL_STEEL(element))
704 if (HitReflectingWalls(element, hit_mask))
709 if (HitAbsorbingWalls(element, hit_mask))
715 if (HitElement(element, hit_mask))
720 DrawLaser(rf - 1, DL_LASER_ENABLED);
721 rf = laser.num_edges;
725 if (laser.dest_element != Feld[ELX][ELY])
727 printf("ALARM: laser.dest_element == %d, Feld[ELX][ELY] == %d\n",
728 laser.dest_element, Feld[ELX][ELY]);
732 if (!end && !laser.stops_inside_element && !StepBehind())
735 printf("ScanLaser: Go one step back\n");
741 AddLaserEdge(LX, LY);
745 DrawLaser(rf - 1, DL_LASER_ENABLED);
750 if (!IN_LEV_FIELD(ELX, ELY))
751 printf("WARNING! (2) %d, %d\n", ELX, ELY);
755 void DrawLaserExt(int start_edge, int num_edges, int mode)
761 printf("DrawLaserExt: start_edge, num_edges, mode == %d, %d, %d\n",
762 start_edge, num_edges, mode);
767 Error(ERR_WARN, "DrawLaserExt: start_edge < 0");
774 Error(ERR_WARN, "DrawLaserExt: num_edges < 0");
780 if (mode == DL_LASER_DISABLED)
782 printf("DrawLaser: Delete laser from edge %d\n", start_edge);
786 /* now draw the laser to the backbuffer and (if enabled) to the screen */
787 DrawLines(drawto, &laser.edge[start_edge], num_edges,
788 (mode == DL_LASER_ENABLED ? pen_ray : pen_bg));
790 redraw_mask |= REDRAW_FIELD;
792 if (mode == DL_LASER_ENABLED)
795 /* after the laser was deleted, the "damaged" graphics must be restored */
796 if (laser.num_damages)
798 int damage_start = 0;
801 /* determine the starting edge, from which graphics need to be restored */
804 for (i = 0; i < laser.num_damages; i++)
806 if (laser.damage[i].edge == start_edge + 1)
815 /* restore graphics from this starting edge to the end of damage list */
816 for (i = damage_start; i < laser.num_damages; i++)
818 int lx = laser.damage[i].x;
819 int ly = laser.damage[i].y;
820 int element = Feld[lx][ly];
822 if (Hit[lx][ly] == laser.damage[i].edge)
823 if (!((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
826 if (Box[lx][ly] == laser.damage[i].edge)
829 if (IS_DRAWABLE(element))
830 DrawField_MM(lx, ly);
833 elx = laser.damage[damage_start].x;
834 ely = laser.damage[damage_start].y;
835 element = Feld[elx][ely];
838 if (IS_BEAMER(element))
842 for (i = 0; i < laser.num_beamers; i++)
843 printf("-> %d\n", laser.beamer_edge[i]);
844 printf("DrawLaserExt: IS_BEAMER: [%d]: Hit[%d][%d] == %d [%d]\n",
845 mode, elx, ely, Hit[elx][ely], start_edge);
846 printf("DrawLaserExt: IS_BEAMER: %d / %d\n",
847 get_element_angle(element), laser.damage[damage_start].angle);
851 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
852 laser.num_beamers > 0 &&
853 start_edge == laser.beamer_edge[laser.num_beamers - 1])
855 /* element is outgoing beamer */
856 laser.num_damages = damage_start + 1;
858 if (IS_BEAMER(element))
859 laser.current_angle = get_element_angle(element);
863 /* element is incoming beamer or other element */
864 laser.num_damages = damage_start;
865 laser.current_angle = laser.damage[laser.num_damages].angle;
870 /* no damages but McDuffin himself (who needs to be redrawn anyway) */
872 elx = laser.start_edge.x;
873 ely = laser.start_edge.y;
874 element = Feld[elx][ely];
877 laser.num_edges = start_edge + 1;
879 laser.current_angle = laser.start_angle;
881 LX = laser.edge[start_edge].x - (SX + 2);
882 LY = laser.edge[start_edge].y - (SY + 2);
883 XS = 2 * Step[laser.current_angle].x;
884 YS = 2 * Step[laser.current_angle].y;
887 printf("DrawLaser: Set (LX, LY) to (%d, %d) [%d]\n",
893 if (IS_BEAMER(element) ||
894 IS_FIBRE_OPTIC(element) ||
895 IS_PACMAN(element) ||
897 IS_POLAR_CROSS(element) ||
898 element == EL_FUSE_ON)
903 printf("element == %d\n", element);
906 if (IS_22_5_ANGLE(laser.current_angle)) /* neither 90° nor 45° angle */
907 step_size = ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) ? 4 : 3);
911 if (IS_POLAR(element) || IS_POLAR_CROSS(element) ||
912 ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
913 (laser.num_beamers == 0 ||
914 start_edge != laser.beamer_edge[laser.num_beamers - 1])))
916 /* element is incoming beamer or other element */
917 step_size = -step_size;
922 if (IS_BEAMER(element))
924 printf("start_edge == %d, laser.beamer_edge == %d\n",
925 start_edge, laser.beamer_edge);
929 LX += step_size * XS;
930 LY += step_size * YS;
932 else if (element != EL_EMPTY)
941 printf("DrawLaser: Finally: (LX, LY) to (%d, %d) [%d]\n",
946 void DrawLaser(int start_edge, int mode)
948 if (laser.num_edges - start_edge < 0)
950 Error(ERR_WARN, "DrawLaser: laser.num_edges - start_edge < 0");
955 /* check if laser is interrupted by beamer element */
956 if (laser.num_beamers > 0 &&
957 start_edge < laser.beamer_edge[laser.num_beamers - 1])
959 if (mode == DL_LASER_ENABLED)
962 int tmp_start_edge = start_edge;
964 /* draw laser segments forward from the start to the last beamer */
965 for (i = 0; i < laser.num_beamers; i++)
967 int tmp_num_edges = laser.beamer_edge[i] - tmp_start_edge;
969 if (tmp_num_edges <= 0)
973 printf("DrawLaser: DL_LASER_ENABLED: i==%d: %d, %d\n",
974 i, laser.beamer_edge[i], tmp_start_edge);
977 DrawLaserExt(tmp_start_edge, tmp_num_edges, DL_LASER_ENABLED);
979 tmp_start_edge = laser.beamer_edge[i];
982 /* draw last segment from last beamer to the end */
983 DrawLaserExt(tmp_start_edge, laser.num_edges - tmp_start_edge,
989 int last_num_edges = laser.num_edges;
990 int num_beamers = laser.num_beamers;
992 /* delete laser segments backward from the end to the first beamer */
993 for (i = num_beamers-1; i >= 0; i--)
995 int tmp_num_edges = last_num_edges - laser.beamer_edge[i];
997 if (laser.beamer_edge[i] - start_edge <= 0)
1000 DrawLaserExt(laser.beamer_edge[i], tmp_num_edges, DL_LASER_DISABLED);
1002 last_num_edges = laser.beamer_edge[i];
1003 laser.num_beamers--;
1007 if (last_num_edges - start_edge <= 0)
1008 printf("DrawLaser: DL_LASER_DISABLED: %d, %d\n",
1009 last_num_edges, start_edge);
1012 /* delete first segment from start to the first beamer */
1013 DrawLaserExt(start_edge, last_num_edges - start_edge, DL_LASER_DISABLED);
1018 DrawLaserExt(start_edge, laser.num_edges - start_edge, mode);
1022 boolean HitElement(int element, int hit_mask)
1024 if (HitOnlyAnEdge(element, hit_mask))
1027 if (IS_MOVING(ELX, ELY) || IS_BLOCKED(ELX, ELY))
1028 element = MovingOrBlocked2Element_MM(ELX, ELY);
1031 printf("HitElement (1): element == %d\n", element);
1035 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1036 printf("HitElement (%d): EXACT MATCH @ (%d, %d)\n", element, ELX, ELY);
1038 printf("HitElement (%d): FUZZY MATCH @ (%d, %d)\n", element, ELX, ELY);
1041 AddDamagedField(ELX, ELY);
1043 /* this is more precise: check if laser would go through the center */
1044 if ((ELX * TILEX + 14 - LX) * YS != (ELY * TILEY + 14 - LY) * XS)
1046 /* skip the whole element before continuing the scan */
1052 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1054 if (LX/TILEX > ELX || LY/TILEY > ELY)
1056 /* skipping scan positions to the right and down skips one scan
1057 position too much, because this is only the top left scan position
1058 of totally four scan positions (plus one to the right, one to the
1059 bottom and one to the bottom right) */
1069 printf("HitElement (2): element == %d\n", element);
1072 if (LX + 5 * XS < 0 ||
1082 printf("HitElement (3): element == %d\n", element);
1085 if (IS_POLAR(element) &&
1086 ((element - EL_POLAR_START) % 2 ||
1087 (element - EL_POLAR_START) / 2 != laser.current_angle % 8))
1089 PlaySoundStereo(SND_KINK, ST(ELX));
1091 laser.num_damages--;
1096 if (IS_POLAR_CROSS(element) &&
1097 (element - EL_POLAR_CROSS_START) != laser.current_angle % 4)
1099 PlaySoundStereo(SND_KINK, ST(ELX));
1101 laser.num_damages--;
1106 if (!IS_BEAMER(element) &&
1107 !IS_FIBRE_OPTIC(element) &&
1108 !IS_GRID_WOOD(element) &&
1109 element != EL_FUEL_EMPTY)
1112 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1113 printf("EXACT MATCH @ (%d, %d)\n", ELX, ELY);
1115 printf("FUZZY MATCH @ (%d, %d)\n", ELX, ELY);
1118 LX = ELX * TILEX + 14;
1119 LY = ELY * TILEY + 14;
1121 AddLaserEdge(LX, LY);
1124 if (IS_MIRROR(element) ||
1125 IS_MIRROR_FIXED(element) ||
1126 IS_POLAR(element) ||
1127 IS_POLAR_CROSS(element) ||
1128 IS_DF_MIRROR(element) ||
1129 IS_DF_MIRROR_AUTO(element) ||
1130 element == EL_PRISM ||
1131 element == EL_REFRACTOR)
1133 int current_angle = laser.current_angle;
1136 laser.num_damages--;
1138 AddDamagedField(ELX, ELY);
1140 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1143 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1145 if (IS_MIRROR(element) ||
1146 IS_MIRROR_FIXED(element) ||
1147 IS_DF_MIRROR(element) ||
1148 IS_DF_MIRROR_AUTO(element))
1149 laser.current_angle = get_mirrored_angle(laser.current_angle,
1150 get_element_angle(element));
1152 if (element == EL_PRISM || element == EL_REFRACTOR)
1153 laser.current_angle = RND(16);
1155 XS = 2 * Step[laser.current_angle].x;
1156 YS = 2 * Step[laser.current_angle].y;
1158 if (!IS_22_5_ANGLE(laser.current_angle)) /* 90° or 45° angle */
1163 LX += step_size * XS;
1164 LY += step_size * YS;
1167 /* draw sparkles on mirror */
1168 if ((IS_MIRROR(element) || IS_MIRROR_FIXED(element)) &&
1169 current_angle != laser.current_angle)
1171 MoveSprite(vp, &Pfeil[2], 4 + 16 * ELX, 5 + 16 * ELY + 1);
1175 if ((!IS_POLAR(element) && !IS_POLAR_CROSS(element)) &&
1176 current_angle != laser.current_angle)
1177 PlaySoundStereo(SND_LASER, ST(ELX));
1180 (get_opposite_angle(laser.current_angle) ==
1181 laser.damage[laser.num_damages - 1].angle ? TRUE : FALSE);
1183 return (laser.overloaded ? TRUE : FALSE);
1186 if (element == EL_FUEL_FULL)
1188 laser.stops_inside_element = TRUE;
1193 if (element == EL_BOMB || element == EL_MINE)
1195 PlaySoundStereo(SND_KINK, ST(ELX));
1197 if (element == EL_MINE)
1198 laser.overloaded = TRUE;
1201 if (element == EL_KETTLE ||
1202 element == EL_CELL ||
1203 element == EL_KEY ||
1204 element == EL_LIGHTBALL ||
1205 element == EL_PACMAN ||
1208 if (!IS_PACMAN(element))
1211 if (element == EL_PACMAN)
1214 if (element == EL_KETTLE || element == EL_CELL)
1216 if (game_mm.kettles_still_needed > 0)
1217 game_mm.kettles_still_needed--;
1221 if (game_mm.kettles_still_needed == 0)
1224 static int xy[4][2] =
1232 PlaySoundStereo(SND_KLING, ST(ELX));
1234 for (y = 0; y < lev_fieldy; y++)
1236 for (x = 0; x < lev_fieldx; x++)
1238 /* initiate opening animation of exit door */
1239 if (Feld[x][y] == EL_EXIT_CLOSED)
1240 Feld[x][y] = EL_EXIT_OPENING;
1242 /* remove field that blocks receiver */
1243 if (IS_RECEIVER(Feld[x][y]))
1245 int phase = Feld[x][y] - EL_RECEIVER_START;
1246 int blocking_x, blocking_y;
1248 blocking_x = x + xy[phase][0];
1249 blocking_y = y + xy[phase][1];
1251 if (IN_LEV_FIELD(blocking_x, blocking_y))
1253 Feld[blocking_x][blocking_y] = EL_EMPTY;
1255 DrawField_MM(blocking_x, blocking_y);
1261 DrawLaser(0, DL_LASER_ENABLED);
1264 else if (element == EL_KEY)
1268 else if (element == EL_LIGHTBALL)
1272 else if (IS_PACMAN(element))
1274 DeletePacMan(ELX, ELY);
1281 if (element == EL_LIGHTBULB_OFF || element == EL_LIGHTBULB_ON)
1283 PlaySoundStereo(SND_KINK, ST(ELX));
1285 DrawLaser(0, DL_LASER_ENABLED);
1287 if (Feld[ELX][ELY] == EL_LIGHTBULB_OFF)
1289 Feld[ELX][ELY] = EL_LIGHTBULB_ON;
1290 game_mm.lights_still_needed--;
1294 Feld[ELX][ELY] = EL_LIGHTBULB_OFF;
1295 game_mm.lights_still_needed++;
1298 DrawField_MM(ELX, ELY);
1299 DrawLaser(0, DL_LASER_ENABLED);
1304 laser.stops_inside_element = TRUE;
1310 printf("HitElement (4): element == %d\n", element);
1313 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1314 laser.num_beamers < MAX_NUM_BEAMERS &&
1315 laser.beamer[BEAMER_NR(element)][1].num)
1317 int beamer_angle = get_element_angle(element);
1318 int beamer_nr = BEAMER_NR(element);
1322 printf("HitElement (BEAMER): element == %d\n", element);
1325 laser.num_damages--;
1327 if (IS_FIBRE_OPTIC(element) ||
1328 laser.current_angle == get_opposite_angle(beamer_angle))
1332 LX = ELX * TILEX + 14;
1333 LY = ELY * TILEY + 14;
1335 AddLaserEdge(LX, LY);
1336 AddDamagedField(ELX, ELY);
1338 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1341 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1343 pos = (ELX == laser.beamer[beamer_nr][0].x &&
1344 ELY == laser.beamer[beamer_nr][0].y ? 1 : 0);
1345 ELX = laser.beamer[beamer_nr][pos].x;
1346 ELY = laser.beamer[beamer_nr][pos].y;
1347 LX = ELX * TILEX + 14;
1348 LY = ELY * TILEY + 14;
1350 if (IS_BEAMER(element))
1352 laser.current_angle = get_element_angle(Feld[ELX][ELY]);
1353 XS = 2 * Step[laser.current_angle].x;
1354 YS = 2 * Step[laser.current_angle].y;
1357 laser.beamer_edge[laser.num_beamers] = laser.num_edges;
1359 AddLaserEdge(LX, LY);
1360 AddDamagedField(ELX, ELY);
1362 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1365 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1367 if (laser.current_angle == (laser.current_angle >> 1) << 1)
1372 LX += step_size * XS;
1373 LY += step_size * YS;
1375 laser.num_beamers++;
1384 boolean HitOnlyAnEdge(int element, int hit_mask)
1386 /* check if the laser hit only the edge of an element and, if so, go on */
1389 printf("LX, LY, hit_mask == %d, %d, %d\n", LX, LY, hit_mask);
1392 if ((hit_mask == HIT_MASK_TOPLEFT ||
1393 hit_mask == HIT_MASK_TOPRIGHT ||
1394 hit_mask == HIT_MASK_BOTTOMLEFT ||
1395 hit_mask == HIT_MASK_BOTTOMRIGHT) &&
1396 laser.current_angle % 4) /* angle is not 90° */
1400 if (hit_mask == HIT_MASK_TOPLEFT)
1405 else if (hit_mask == HIT_MASK_TOPRIGHT)
1410 else if (hit_mask == HIT_MASK_BOTTOMLEFT)
1415 else /* (hit_mask == HIT_MASK_BOTTOMRIGHT) */
1421 AddDamagedField((LX + 2 * dx) / TILEX, (LY + 2 * dy) / TILEY);
1427 printf("[HitOnlyAnEdge() == TRUE]\n");
1434 printf("[HitOnlyAnEdge() == FALSE]\n");
1440 boolean HitPolarizer(int element, int hit_mask)
1442 if (HitOnlyAnEdge(element, hit_mask))
1445 if (IS_DF_GRID(element))
1447 int grid_angle = get_element_angle(element);
1450 printf("HitPolarizer: angle: grid == %d, laser == %d\n",
1451 grid_angle, laser.current_angle);
1454 AddLaserEdge(LX, LY);
1455 AddDamagedField(ELX, ELY);
1458 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1460 if (laser.current_angle == grid_angle ||
1461 laser.current_angle == get_opposite_angle(grid_angle))
1463 /* skip the whole element before continuing the scan */
1469 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1471 if (LX/TILEX > ELX || LY/TILEY > ELY)
1473 /* skipping scan positions to the right and down skips one scan
1474 position too much, because this is only the top left scan position
1475 of totally four scan positions (plus one to the right, one to the
1476 bottom and one to the bottom right) */
1482 AddLaserEdge(LX, LY);
1488 printf("HitPolarizer: LX, LY == %d, %d [%d, %d] [%d, %d]\n",
1490 LX / TILEX, LY / TILEY,
1491 LX % TILEX, LY % TILEY);
1496 else if (IS_GRID_STEEL_FIXED(element) || IS_GRID_STEEL_AUTO(element))
1498 return HitReflectingWalls(element, hit_mask);
1502 return HitAbsorbingWalls(element, hit_mask);
1505 else if (IS_GRID_STEEL(element))
1507 return HitReflectingWalls(element, hit_mask);
1509 else /* IS_GRID_WOOD */
1511 return HitAbsorbingWalls(element, hit_mask);
1517 boolean HitBlock(int element, int hit_mask)
1519 boolean check = FALSE;
1521 if ((element == EL_GATE_STONE || element == EL_GATE_WOOD) &&
1522 game_mm.num_keys == 0)
1525 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1528 int ex = ELX * TILEX + 14;
1529 int ey = ELY * TILEY + 14;
1533 for (i = 1; i < 32; i++)
1538 if ((x == ex || x == ex + 1) && (y == ey || y == ey + 1))
1543 if (check && (element == EL_BLOCK_WOOD || element == EL_GATE_WOOD))
1544 return HitAbsorbingWalls(element, hit_mask);
1548 AddLaserEdge(LX - XS, LY - YS);
1549 AddDamagedField(ELX, ELY);
1552 Box[ELX][ELY] = laser.num_edges;
1554 return HitReflectingWalls(element, hit_mask);
1557 if (element == EL_GATE_STONE || element == EL_GATE_WOOD)
1559 int xs = XS / 2, ys = YS / 2;
1560 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1561 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1563 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1564 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1566 laser.overloaded = (element == EL_GATE_STONE);
1571 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1572 (hit_mask == HIT_MASK_TOP ||
1573 hit_mask == HIT_MASK_LEFT ||
1574 hit_mask == HIT_MASK_RIGHT ||
1575 hit_mask == HIT_MASK_BOTTOM))
1576 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1577 hit_mask == HIT_MASK_BOTTOM),
1578 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1579 hit_mask == HIT_MASK_RIGHT));
1580 AddLaserEdge(LX, LY);
1586 if (element == EL_GATE_STONE && Box[ELX][ELY])
1588 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
1600 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1602 int xs = XS / 2, ys = YS / 2;
1603 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1604 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1606 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1607 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1609 laser.overloaded = (element == EL_BLOCK_STONE);
1614 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1615 (hit_mask == HIT_MASK_TOP ||
1616 hit_mask == HIT_MASK_LEFT ||
1617 hit_mask == HIT_MASK_RIGHT ||
1618 hit_mask == HIT_MASK_BOTTOM))
1619 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1620 hit_mask == HIT_MASK_BOTTOM),
1621 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1622 hit_mask == HIT_MASK_RIGHT));
1623 AddDamagedField(ELX, ELY);
1625 LX = ELX * TILEX + 14;
1626 LY = ELY * TILEY + 14;
1628 AddLaserEdge(LX, LY);
1630 laser.stops_inside_element = TRUE;
1638 boolean HitLaserSource(int element, int hit_mask)
1640 if (HitOnlyAnEdge(element, hit_mask))
1643 PlaySoundStereo(SND_AUTSCH, ST(ELX));
1644 laser.overloaded = TRUE;
1649 boolean HitLaserDestination(int element, int hit_mask)
1651 if (HitOnlyAnEdge(element, hit_mask))
1654 if (element != EL_EXIT_OPEN &&
1655 !(IS_RECEIVER(element) &&
1656 game_mm.kettles_still_needed == 0 &&
1657 laser.current_angle == get_opposite_angle(get_element_angle(element))))
1659 PlaySoundStereo(SND_HOLZ, ST(ELX));
1663 if (IS_RECEIVER(element) ||
1664 (IS_22_5_ANGLE(laser.current_angle) &&
1665 (ELX != (LX + 6 * XS) / TILEX ||
1666 ELY != (LY + 6 * YS) / TILEY ||
1675 LX = ELX * TILEX + 14;
1676 LY = ELY * TILEY + 14;
1678 laser.stops_inside_element = TRUE;
1681 AddLaserEdge(LX, LY);
1682 AddDamagedField(ELX, ELY);
1684 if (game_mm.lights_still_needed == 0)
1685 game_mm.level_solved = TRUE;
1690 boolean HitReflectingWalls(int element, int hit_mask)
1692 /* check if laser hits side of a wall with an angle that is not 90° */
1693 if (!IS_90_ANGLE(laser.current_angle) && (hit_mask == HIT_MASK_TOP ||
1694 hit_mask == HIT_MASK_LEFT ||
1695 hit_mask == HIT_MASK_RIGHT ||
1696 hit_mask == HIT_MASK_BOTTOM))
1698 PlaySoundStereo(SND_HUI, ST(ELX));
1703 if (!IS_DF_GRID(element))
1704 AddLaserEdge(LX, LY);
1706 /* check if laser hits wall with an angle of 45° */
1707 if (!IS_22_5_ANGLE(laser.current_angle))
1709 if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1712 laser.current_angle = get_mirrored_angle(laser.current_angle,
1715 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1718 laser.current_angle = get_mirrored_angle(laser.current_angle,
1722 AddLaserEdge(LX, LY);
1724 XS = 2 * Step[laser.current_angle].x;
1725 YS = 2 * Step[laser.current_angle].y;
1729 else if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1731 laser.current_angle = get_mirrored_angle(laser.current_angle,
1736 if (!IS_DF_GRID(element))
1737 AddLaserEdge(LX, LY);
1742 if (!IS_DF_GRID(element))
1743 AddLaserEdge(LX, LY + YS / 2);
1746 if (!IS_DF_GRID(element))
1747 AddLaserEdge(LX, LY);
1750 YS = 2 * Step[laser.current_angle].y;
1754 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1756 laser.current_angle = get_mirrored_angle(laser.current_angle,
1761 if (!IS_DF_GRID(element))
1762 AddLaserEdge(LX, LY);
1767 if (!IS_DF_GRID(element))
1768 AddLaserEdge(LX + XS / 2, LY);
1771 if (!IS_DF_GRID(element))
1772 AddLaserEdge(LX, LY);
1775 XS = 2 * Step[laser.current_angle].x;
1781 /* reflection at the edge of reflecting DF style wall */
1782 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1784 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1785 hit_mask == HIT_MASK_TOPRIGHT) ||
1786 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1787 hit_mask == HIT_MASK_TOPLEFT) ||
1788 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1789 hit_mask == HIT_MASK_BOTTOMLEFT) ||
1790 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1791 hit_mask == HIT_MASK_BOTTOMRIGHT))
1794 (hit_mask == HIT_MASK_TOPRIGHT || hit_mask == HIT_MASK_BOTTOMLEFT ?
1795 ANG_MIRROR_135 : ANG_MIRROR_45);
1797 PlaySoundStereo(SND_HUI, ST(ELX));
1799 AddDamagedField(ELX, ELY);
1800 AddLaserEdge(LX, LY);
1802 laser.current_angle = get_mirrored_angle(laser.current_angle,
1810 AddLaserEdge(LX, LY);
1816 /* reflection inside an edge of reflecting DF style wall */
1817 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1819 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1820 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT)) ||
1821 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1822 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMRIGHT)) ||
1823 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1824 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT)) ||
1825 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1826 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPLEFT)))
1829 (hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT) ||
1830 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT) ?
1831 ANG_MIRROR_135 : ANG_MIRROR_45);
1833 PlaySoundStereo(SND_HUI, ST(ELX));
1836 AddDamagedField(ELX, ELY);
1839 AddLaserEdge(LX - XS, LY - YS);
1840 AddLaserEdge(LX - XS + (ABS(XS) == 4 ? XS/2 : 0),
1841 LY - YS + (ABS(YS) == 4 ? YS/2 : 0));
1843 laser.current_angle = get_mirrored_angle(laser.current_angle,
1851 AddLaserEdge(LX, LY);
1857 /* check if laser hits DF style wall with an angle of 90° */
1858 if (IS_DF_WALL(element) && IS_90_ANGLE(laser.current_angle))
1860 if ((IS_HORIZ_ANGLE(laser.current_angle) &&
1861 (!(hit_mask & HIT_MASK_TOP) || !(hit_mask & HIT_MASK_BOTTOM))) ||
1862 (IS_VERT_ANGLE(laser.current_angle) &&
1863 (!(hit_mask & HIT_MASK_LEFT) || !(hit_mask & HIT_MASK_RIGHT))))
1865 static int last_LX = 0, last_LY = 0, last_hit_mask = 0;
1867 /* laser at last step touched nothing or the same side of the wall */
1868 if (LX != last_LX || LY != last_LY || hit_mask == last_hit_mask)
1870 AddDamagedField(ELX, ELY);
1877 last_hit_mask = hit_mask;
1884 if (!HitOnlyAnEdge(element, hit_mask))
1886 laser.overloaded = TRUE;
1894 boolean HitAbsorbingWalls(int element, int hit_mask)
1896 if (HitOnlyAnEdge(element, hit_mask))
1900 (hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT))
1902 AddLaserEdge(LX - XS, LY - YS);
1909 (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM))
1911 AddLaserEdge(LX - XS, LY - YS);
1917 if (IS_WALL_WOOD(element) ||
1918 IS_DF_WALL_WOOD(element) ||
1919 IS_GRID_WOOD(element) ||
1920 IS_GRID_WOOD_FIXED(element) ||
1921 IS_GRID_WOOD_AUTO(element) ||
1922 element == EL_FUSE_ON ||
1923 element == EL_BLOCK_WOOD ||
1924 element == EL_GATE_WOOD)
1926 PlaySoundStereo(SND_HOLZ, ST(ELX));
1931 if (IS_WALL_ICE(element))
1935 mask = (LX + XS) / MINI_TILEX - ELX * 2 + 1; /* Quadrant (horizontal) */
1936 mask <<= (((LY + YS) / MINI_TILEY - ELY * 2) > 0) * 2; /* || (vertical) */
1938 /* check if laser hits wall with an angle of 90° */
1939 if (IS_90_ANGLE(laser.current_angle))
1940 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1942 if (mask == 1 || mask == 2 || mask == 4 || mask == 8)
1946 for (i = 0; i < 4; i++)
1948 if (mask == (1 << i) && (XS > 0) == (i % 2) && (YS > 0) == (i / 2))
1949 mask = 15 - (8 >> i);
1950 else if (ABS(XS) == 4 &&
1952 (XS > 0) == (i % 2) &&
1953 (YS < 0) == (i / 2))
1954 mask = 3 + (i / 2) * 9;
1955 else if (ABS(YS) == 4 &&
1957 (XS < 0) == (i % 2) &&
1958 (YS > 0) == (i / 2))
1959 mask = 5 + (i % 2) * 5;
1963 laser.wall_mask = mask;
1965 else if (IS_WALL_AMOEBA(element))
1967 int elx = (LX - 2 * XS) / TILEX;
1968 int ely = (LY - 2 * YS) / TILEY;
1969 int element2 = Feld[elx][ely];
1972 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element2))
1974 laser.dest_element = EL_EMPTY;
1982 mask = (LX - 2 * XS) / 16 - ELX * 2 + 1;
1983 mask <<= ((LY - 2 * YS) / 16 - ELY * 2) * 2;
1985 if (IS_90_ANGLE(laser.current_angle))
1986 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1988 laser.dest_element = element2 | EL_WALL_AMOEBA;
1990 laser.wall_mask = mask;
1996 void OpenExit(int x, int y)
2000 if (!MovDelay[x][y]) /* next animation frame */
2001 MovDelay[x][y] = 4 * delay;
2003 if (MovDelay[x][y]) /* wait some time before next frame */
2008 phase = MovDelay[x][y] / delay;
2010 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2011 DrawGraphicAnimation_MM(x, y, IMG_MM_EXIT_OPENING, 3 - phase);
2013 if (!MovDelay[x][y])
2015 Feld[x][y] = EL_EXIT_OPEN;
2021 void OpenSurpriseBall(int x, int y)
2025 if (!MovDelay[x][y]) /* next animation frame */
2026 MovDelay[x][y] = 50 * delay;
2028 if (MovDelay[x][y]) /* wait some time before next frame */
2032 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2035 int graphic = el2gfx(Store[x][y]);
2037 int dx = RND(26), dy = RND(26);
2039 getGraphicSource(graphic, 0, &bitmap, &gx, &gy);
2041 BlitBitmap(bitmap, drawto, gx + dx, gy + dy, 6, 6,
2042 SX + x * TILEX + dx, SY + y * TILEY + dy);
2044 MarkTileDirty(x, y);
2047 if (!MovDelay[x][y])
2049 Feld[x][y] = Store[x][y];
2058 void MeltIce(int x, int y)
2063 if (!MovDelay[x][y]) /* next animation frame */
2064 MovDelay[x][y] = frames * delay;
2066 if (MovDelay[x][y]) /* wait some time before next frame */
2069 int wall_mask = Store2[x][y];
2070 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_ICE;
2073 phase = frames - MovDelay[x][y] / delay - 1;
2075 if (!MovDelay[x][y])
2079 Feld[x][y] = real_element & (wall_mask ^ 0xFF);
2080 Store[x][y] = Store2[x][y] = 0;
2082 DrawWalls_MM(x, y, Feld[x][y]);
2084 if (Feld[x][y] == EL_WALL_ICE)
2085 Feld[x][y] = EL_EMPTY;
2087 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
2088 if (laser.damage[i].is_mirror)
2092 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
2094 DrawLaser(0, DL_LASER_DISABLED);
2098 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2100 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2102 laser.redraw = TRUE;
2107 void GrowAmoeba(int x, int y)
2112 if (!MovDelay[x][y]) /* next animation frame */
2113 MovDelay[x][y] = frames * delay;
2115 if (MovDelay[x][y]) /* wait some time before next frame */
2118 int wall_mask = Store2[x][y];
2119 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_AMOEBA;
2122 phase = MovDelay[x][y] / delay;
2124 if (!MovDelay[x][y])
2126 Feld[x][y] = real_element;
2127 Store[x][y] = Store2[x][y] = 0;
2129 DrawWalls_MM(x, y, Feld[x][y]);
2130 DrawLaser(0, DL_LASER_ENABLED);
2132 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2134 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2139 static void Explode_MM(int x, int y, int phase, int mode)
2141 int num_phase = 9, delay = 2;
2142 int last_phase = num_phase * delay;
2143 int half_phase = (num_phase / 2) * delay;
2145 laser.redraw = TRUE;
2147 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2149 int center_element = Feld[x][y];
2151 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2153 /* put moving element to center field (and let it explode there) */
2154 center_element = MovingOrBlocked2Element_MM(x, y);
2155 RemoveMovingField_MM(x, y);
2157 Feld[x][y] = center_element;
2160 if (center_element == EL_BOMB || IS_MCDUFFIN(center_element))
2161 Store[x][y] = center_element;
2163 Store[x][y] = EL_EMPTY;
2165 Store2[x][y] = mode;
2166 Feld[x][y] = EL_EXPLODING_OPAQUE;
2167 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2173 Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
2175 if (phase == half_phase)
2177 Feld[x][y] = EL_EXPLODING_TRANSP;
2179 if (x == ELX && y == ELY)
2183 if (phase == last_phase)
2185 if (Store[x][y] == EL_BOMB)
2187 laser.num_damages--;
2188 DrawLaser(0, DL_LASER_DISABLED);
2189 laser.num_edges = 0;
2191 Bang_MM(laser.start_edge.x, laser.start_edge.y);
2192 Store[x][y] = EL_EMPTY;
2194 else if (IS_MCDUFFIN(Store[x][y]))
2196 game_mm.game_over = TRUE;
2197 game_mm.game_over_cause = GAME_OVER_BOMB;
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 PlaySoundStereo(SND_QUIEK, ST(x));
2280 else if (element == EL_BOMB || IS_MCDUFFIN(element))
2281 PlaySoundStereo(SND_ROAAAR, ST(x));
2282 else if (element == EL_KEY)
2283 PlaySoundStereo(SND_KLING, ST(x));
2285 PlaySoundStereo((mode == EX_SHORT ? SND_WHOOSH : SND_KABUMM), ST(x));
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 mx, int my, int button)
2444 static unsigned int click_delay = 0;
2445 static int click_delay_value = CLICK_DELAY_SHORT;
2446 static boolean new_button = TRUE;
2448 int x = (mx - SX) / TILEX, y = (my - SY) / TILEY;
2450 /* do not rotate objects hit by the laser after the game was solved */
2451 if (game_mm.level_solved && Hit[x][y])
2454 if (button == MB_RELEASED)
2457 click_delay_value = CLICK_DELAY_SHORT;
2459 /* release eventually hold auto-rotating mirror */
2460 RotateMirror(x, y, MB_RELEASED);
2465 if (!DelayReached(&click_delay, click_delay_value) && !new_button)
2468 if (button == MB_MIDDLEBUTTON) /* middle button has no function */
2471 if (!IN_PIX_FIELD(mx - SX, my - SY))
2474 if (Feld[x][y] == EL_EMPTY)
2477 element = Feld[x][y];
2479 if (IS_MIRROR(element) ||
2480 IS_BEAMER(element) ||
2481 IS_POLAR(element) ||
2482 IS_POLAR_CROSS(element) ||
2483 IS_DF_MIRROR(element) ||
2484 IS_DF_MIRROR_AUTO(element))
2486 RotateMirror(x, y, button);
2488 else if (IS_MCDUFFIN(element))
2490 if (!laser.fuse_off)
2492 DrawLaser(0, DL_LASER_DISABLED);
2499 element = get_rotated_element(element, BUTTON_ROTATION(button));
2500 laser.start_angle = get_element_angle(element);
2504 Feld[x][y] = element;
2511 if (!laser.fuse_off)
2514 else if (element == EL_FUSE_ON && laser.fuse_off)
2516 if (x != laser.fuse_x || y != laser.fuse_y)
2519 laser.fuse_off = FALSE;
2520 laser.fuse_x = laser.fuse_y = -1;
2522 DrawGraphic_MM(x, y, IMG_MM_FUSE_ACTIVE);
2525 else if (element == EL_FUSE_ON && !laser.fuse_off && new_button)
2527 laser.fuse_off = TRUE;
2530 laser.overloaded = FALSE;
2532 DrawLaser(0, DL_LASER_DISABLED);
2533 DrawGraphic_MM(x, y, IMG_MM_FUSE);
2535 else if (element == EL_LIGHTBALL)
2539 DrawLaser(0, DL_LASER_ENABLED);
2542 click_delay_value = (new_button ? CLICK_DELAY_LONG : CLICK_DELAY_SHORT);
2546 void RotateMirror(int x, int y, int button)
2548 static int hold_x = -1, hold_y = -1;
2550 if (button == MB_RELEASED)
2552 /* release eventually hold auto-rotating mirror */
2559 if (IS_MIRROR(Feld[x][y]) ||
2560 IS_POLAR_CROSS(Feld[x][y]) ||
2561 IS_POLAR(Feld[x][y]) ||
2562 IS_BEAMER(Feld[x][y]) ||
2563 IS_DF_MIRROR(Feld[x][y]) ||
2564 IS_GRID_STEEL_AUTO(Feld[x][y]) ||
2565 IS_GRID_WOOD_AUTO(Feld[x][y]))
2567 Feld[x][y] = get_rotated_element(Feld[x][y], BUTTON_ROTATION(button));
2569 else if (IS_DF_MIRROR_AUTO(Feld[x][y]))
2571 if (button == MB_LEFTBUTTON)
2573 /* left mouse button only for manual adjustment, no auto-rotating;
2574 freeze mirror for until mouse button released */
2578 else if (button == MB_RIGHTBUTTON && (hold_x != x || hold_y != y))
2580 Feld[x][y] = get_rotated_element(Feld[x][y], ROTATE_RIGHT);
2584 if (IS_GRID_STEEL_AUTO(Feld[x][y]) || IS_GRID_WOOD_AUTO(Feld[x][y]))
2586 int edge = Hit[x][y];
2592 DrawLaser(edge - 1, DL_LASER_DISABLED);
2596 else if (ObjHit(x, y, HIT_POS_CENTER))
2598 int edge = Hit[x][y];
2602 Error(ERR_WARN, "RotateMirror: inconsistent field Hit[][]!\n");
2606 DrawLaser(edge - 1, DL_LASER_DISABLED);
2613 if (ObjHit(x, y, HIT_POS_EDGE | HIT_POS_BETWEEN))
2618 if ((IS_BEAMER(Feld[x][y]) ||
2619 IS_POLAR(Feld[x][y]) ||
2620 IS_POLAR_CROSS(Feld[x][y])) && x == ELX && y == ELY)
2624 if (IS_BEAMER(Feld[x][y]))
2627 printf("TEST (%d, %d) [%d] [%d]\n",
2629 laser.beamer_edge, laser.beamer[1].num);
2639 DrawLaser(0, DL_LASER_ENABLED);
2643 void AutoRotateMirrors()
2645 static unsigned int rotate_delay = 0;
2648 if (!DelayReached(&rotate_delay, AUTO_ROTATE_DELAY))
2651 for (x = 0; x < lev_fieldx; x++)
2653 for (y = 0; y < lev_fieldy; y++)
2655 int element = Feld[x][y];
2657 /* do not rotate objects hit by the laser after the game was solved */
2658 if (game_mm.level_solved && Hit[x][y])
2661 if (IS_DF_MIRROR_AUTO(element) ||
2662 IS_GRID_WOOD_AUTO(element) ||
2663 IS_GRID_STEEL_AUTO(element) ||
2664 element == EL_REFRACTOR)
2665 RotateMirror(x, y, MB_RIGHTBUTTON);
2670 boolean ObjHit(int obx, int oby, int bits)
2677 if (bits & HIT_POS_CENTER)
2679 if (ReadPixel(drawto, SX + obx + 15, SY + oby + 15) == pen_ray)
2683 if (bits & HIT_POS_EDGE)
2685 for (i = 0; i < 4; i++)
2686 if (ReadPixel(drawto,
2687 SX + obx + 31 * (i % 2),
2688 SY + oby + 31 * (i / 2)) == pen_ray)
2692 if (bits & HIT_POS_BETWEEN)
2694 for (i = 0; i < 4; i++)
2695 if (ReadPixel(drawto,
2696 SX + 4 + obx + 22 * (i % 2),
2697 SY + 4 + oby + 22 * (i / 2)) == pen_ray)
2704 void DeletePacMan(int px, int py)
2710 if (game_mm.num_pacman <= 1)
2712 game_mm.num_pacman = 0;
2716 for (i = 0; i < game_mm.num_pacman; i++)
2717 if (game_mm.pacman[i].x == px && game_mm.pacman[i].y == py)
2720 game_mm.num_pacman--;
2722 for (j = i; j < game_mm.num_pacman; j++)
2724 game_mm.pacman[j].x = game_mm.pacman[j + 1].x;
2725 game_mm.pacman[j].y = game_mm.pacman[j + 1].y;
2726 game_mm.pacman[j].dir = game_mm.pacman[j + 1].dir;
2730 void ColorCycling(void)
2732 static int CC, Cc = 0;
2734 static int color, old = 0xF00, new = 0x010, mult = 1;
2735 static unsigned short red, green, blue;
2737 if (color_status == STATIC_COLORS)
2742 if (CC < Cc || CC > Cc + 50)
2746 color = old + new * mult;
2752 if (ABS(mult) == 16)
2762 red = 0x0e00 * ((color & 0xF00) >> 8);
2763 green = 0x0e00 * ((color & 0x0F0) >> 4);
2764 blue = 0x0e00 * ((color & 0x00F));
2765 SetRGB(pen_magicolor[0], red, green, blue);
2767 red = 0x1111 * ((color & 0xF00) >> 8);
2768 green = 0x1111 * ((color & 0x0F0) >> 4);
2769 blue = 0x1111 * ((color & 0x00F));
2770 SetRGB(pen_magicolor[1], red, green, blue);
2774 static void GameActions_MM_Ext(byte action[MAX_PLAYERS], boolean warp_mode)
2776 static unsigned int action_delay = 0;
2777 static unsigned int pacman_delay = 0;
2778 static unsigned int energy_delay = 0;
2779 static unsigned int overload_delay = 0;
2785 WaitUntilDelayReached(&action_delay, GameFrameDelay);
2787 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2790 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2792 element = Feld[x][y];
2794 if (!IS_MOVING(x, y) && CAN_MOVE(element))
2795 StartMoving_MM(x, y);
2796 else if (IS_MOVING(x, y))
2797 ContinueMoving_MM(x, y);
2798 else if (IS_EXPLODING(element))
2799 Explode_MM(x, y, Frame[x][y], EX_NORMAL);
2800 else if (element == EL_EXIT_OPENING)
2802 else if (element == EL_GRAY_BALL_OPENING)
2803 OpenSurpriseBall(x, y);
2804 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_ICE)
2806 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_AMOEBA)
2810 AutoRotateMirrors();
2813 /* !!! CHANGE THIS: REDRAW ONLY WHEN NEEDED !!! */
2815 /* redraw after Explode_MM() ... */
2817 DrawLaser(0, DL_LASER_ENABLED);
2818 laser.redraw = FALSE;
2823 if (game_mm.num_pacman && DelayReached(&pacman_delay, 250))
2827 if (laser.num_damages > MAX_LASER_LEN && !laser.fuse_off)
2829 DrawLaser(0, DL_LASER_DISABLED);
2834 if (DelayReached(&energy_delay, 4000))
2836 game_mm.energy_left--;
2837 if (game_mm.energy_left >= 0)
2840 BlitBitmap(pix[PIX_DOOR], drawto,
2841 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
2842 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
2843 DX_ENERGY, DY_ENERGY);
2845 redraw_mask |= REDRAW_DOOR_1;
2847 else if (setup.time_limit)
2851 for (i = 15; i >= 0; i--)
2854 SetRGB(pen_ray, 0x0000, 0x0000, i * color_scale);
2856 pen_ray = GetPixelFromRGB(window,
2857 native_mm_level.laser_red * 0x11 * i,
2858 native_mm_level.laser_green * 0x11 * i,
2859 native_mm_level.laser_blue * 0x11 * i);
2861 DrawLaser(0, DL_LASER_ENABLED);
2866 StopSound(SND_WARNTON);
2869 DrawLaser(0, DL_LASER_DISABLED);
2870 game_mm.game_over = TRUE;
2871 game_mm.game_over_cause = GAME_OVER_NO_ENERGY;
2874 if (Request("Out of magic energy ! Play it again ?",
2875 REQ_ASK | REQ_STAY_CLOSED))
2881 game_status = MAINMENU;
2890 element = laser.dest_element;
2893 if (element != Feld[ELX][ELY])
2895 printf("element == %d, Feld[ELX][ELY] == %d\n",
2896 element, Feld[ELX][ELY]);
2900 if (!laser.overloaded && laser.overload_value == 0 &&
2901 element != EL_BOMB &&
2902 element != EL_MINE &&
2903 element != EL_BALL_GRAY &&
2904 element != EL_BLOCK_STONE &&
2905 element != EL_BLOCK_WOOD &&
2906 element != EL_FUSE_ON &&
2907 element != EL_FUEL_FULL &&
2908 !IS_WALL_ICE(element) &&
2909 !IS_WALL_AMOEBA(element))
2912 if (((laser.overloaded && laser.overload_value < MAX_LASER_OVERLOAD) ||
2913 (!laser.overloaded && laser.overload_value > 0)) &&
2914 DelayReached(&overload_delay, 60 + !laser.overloaded * 120))
2916 if (laser.overloaded)
2917 laser.overload_value++;
2919 laser.overload_value--;
2921 if (game_mm.cheat_no_overload)
2923 laser.overloaded = FALSE;
2924 laser.overload_value = 0;
2927 game_mm.laser_overload_value = laser.overload_value;
2929 if (laser.overload_value < MAX_LASER_OVERLOAD - 8)
2931 int color_up = 0xFF * laser.overload_value / MAX_LASER_OVERLOAD;
2932 int color_down = 0xFF - color_up;
2935 SetRGB(pen_ray, (laser.overload_value / 6) * color_scale, 0x0000,
2936 (15 - (laser.overload_value / 6)) * color_scale);
2939 GetPixelFromRGB(window,
2940 (native_mm_level.laser_red ? 0xFF : color_up),
2941 (native_mm_level.laser_green ? color_down : 0x00),
2942 (native_mm_level.laser_blue ? color_down : 0x00));
2944 DrawLaser(0, DL_LASER_ENABLED);
2948 if (laser.overloaded)
2950 if (setup.sound_loops)
2951 PlaySoundExt(SND_WARNTON, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
2952 SND_CTRL_PLAY_LOOP);
2954 PlaySoundStereo(SND_WARNTON, SOUND_MAX_RIGHT);
2957 if (!laser.overloaded)
2958 StopSound(SND_WARNTON);
2960 if (laser.overloaded)
2963 BlitBitmap(pix[PIX_DOOR], drawto,
2964 DOOR_GFX_PAGEX4 + XX_OVERLOAD,
2965 DOOR_GFX_PAGEY1 + YY_OVERLOAD + OVERLOAD_YSIZE
2966 - laser.overload_value,
2967 OVERLOAD_XSIZE, laser.overload_value,
2968 DX_OVERLOAD, DY_OVERLOAD + OVERLOAD_YSIZE
2969 - laser.overload_value);
2971 redraw_mask |= REDRAW_DOOR_1;
2976 BlitBitmap(pix[PIX_DOOR], drawto,
2977 DOOR_GFX_PAGEX5 + XX_OVERLOAD, DOOR_GFX_PAGEY1 + YY_OVERLOAD,
2978 OVERLOAD_XSIZE, OVERLOAD_YSIZE - laser.overload_value,
2979 DX_OVERLOAD, DY_OVERLOAD);
2981 redraw_mask |= REDRAW_DOOR_1;
2984 if (laser.overload_value == MAX_LASER_OVERLOAD)
2988 for (i = 15; i >= 0; i--)
2991 SetRGB(pen_ray, i * color_scale, 0x0000, 0x0000);
2994 pen_ray = GetPixelFromRGB(window, 0x11 * i, 0x00, 0x00);
2996 DrawLaser(0, DL_LASER_ENABLED);
3001 DrawLaser(0, DL_LASER_DISABLED);
3003 game_mm.game_over = TRUE;
3004 game_mm.game_over_cause = GAME_OVER_OVERLOADED;
3007 if (Request("Magic spell hit Mc Duffin ! Play it again ?",
3008 REQ_ASK | REQ_STAY_CLOSED))
3014 game_status = MAINMENU;
3028 if (element == EL_BOMB && CT > 1500)
3030 if (game_mm.cheat_no_explosion)
3034 laser.num_damages--;
3035 DrawLaser(0, DL_LASER_DISABLED);
3036 laser.num_edges = 0;
3041 laser.dest_element = EL_EXPLODING_OPAQUE;
3045 laser.num_damages--;
3046 DrawLaser(0, DL_LASER_DISABLED);
3048 laser.num_edges = 0;
3049 Bang_MM(laser.start_edge.x, laser.start_edge.y);
3051 if (Request("Bomb killed Mc Duffin ! Play it again ?",
3052 REQ_ASK | REQ_STAY_CLOSED))
3058 game_status = MAINMENU;
3066 if (element == EL_FUSE_ON && CT > 500)
3068 laser.fuse_off = TRUE;
3072 DrawLaser(0, DL_LASER_DISABLED);
3073 DrawGraphic_MM(ELX, ELY, IMG_MM_FUSE);
3076 if (element == EL_BALL_GRAY && CT > 1500)
3078 static int new_elements[] =
3081 EL_MIRROR_FIXED_START,
3083 EL_POLAR_CROSS_START,
3089 int num_new_elements = sizeof(new_elements) / sizeof(int);
3090 int new_element = new_elements[RND(num_new_elements)];
3092 Store[ELX][ELY] = new_element + RND(get_num_elements(new_element));
3093 Feld[ELX][ELY] = EL_GRAY_BALL_OPENING;
3095 /* !!! CHECK AGAIN: Laser on Polarizer !!! */
3106 element = EL_MIRROR_START + RND(16);
3112 element = (rnd == 0 ? EL_KETTLE : rnd == 1 ? EL_BOMB : EL_PRISM);
3119 element = (rnd == 0 ? EL_FUSE_ON :
3120 rnd >= 1 && rnd <= 4 ? EL_PACMAN_RIGHT + rnd - 1 :
3121 rnd >= 5 && rnd <= 20 ? EL_POLAR_START + rnd - 5 :
3122 rnd >= 21 && rnd <= 24 ? EL_POLAR_CROSS_START + rnd - 21 :
3123 EL_MIRROR_FIXED_START + rnd - 25);
3128 graphic = el2gfx(element);
3130 for (i = 0; i < 50; i++)
3136 BlitBitmap(pix[PIX_BACK], drawto,
3137 SX + (graphic % GFX_PER_LINE) * TILEX + x,
3138 SY + (graphic / GFX_PER_LINE) * TILEY + y, 6, 6,
3139 SX + ELX * TILEX + x,
3140 SY + ELY * TILEY + y);
3142 MarkTileDirty(ELX, ELY);
3145 DrawLaser(0, DL_LASER_ENABLED);
3150 Feld[ELX][ELY] = element;
3151 DrawField_MM(ELX, ELY);
3154 printf("NEW ELEMENT: (%d, %d)\n", ELX, ELY);
3157 /* above stuff: GRAY BALL -> PRISM !!! */
3159 LX = ELX * TILEX + 14;
3160 LY = ELY * TILEY + 14;
3161 if (laser.current_angle == (laser.current_angle >> 1) << 1)
3168 laser.num_edges -= 2;
3169 laser.num_damages--;
3173 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
3174 if (laser.damage[i].is_mirror)
3178 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3180 DrawLaser(0, DL_LASER_DISABLED);
3182 DrawLaser(0, DL_LASER_DISABLED);
3188 printf("TEST ELEMENT: %d\n", Feld[0][0]);
3195 if (IS_WALL_ICE(element) && CT > 1000)
3197 PlaySoundStereo(SND_SLURP, ST(ELX));
3200 Feld[ELX][ELY] = Feld[ELX][ELY] - EL_WALL_ICE + EL_WALL_CHANGING;
3201 Store[ELX][ELY] = EL_WALL_ICE;
3202 Store2[ELX][ELY] = laser.wall_mask;
3204 laser.dest_element = Feld[ELX][ELY];
3209 for (i = 0; i < 5; i++)
3215 Feld[ELX][ELY] &= (laser.wall_mask ^ 0xFF);
3219 DrawWallsAnimation_MM(ELX, ELY, Feld[ELX][ELY], phase, laser.wall_mask);
3224 if (Feld[ELX][ELY] == EL_WALL_ICE)
3225 Feld[ELX][ELY] = EL_EMPTY;
3229 LX = laser.edge[laser.num_edges].x - (SX + 2);
3230 LY = laser.edge[laser.num_edges].y - (SY + 2);
3233 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
3234 if (laser.damage[i].is_mirror)
3238 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3240 DrawLaser(0, DL_LASER_DISABLED);
3247 if (IS_WALL_AMOEBA(element) && CT > 1200)
3249 int k1, k2, k3, dx, dy, de, dm;
3250 int element2 = Feld[ELX][ELY];
3252 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element))
3255 for (i = laser.num_damages - 1; i >= 0; i--)
3256 if (laser.damage[i].is_mirror)
3259 r = laser.num_edges;
3260 d = laser.num_damages;
3267 DrawLaser(laser.damage[k1].edge - 1, DL_LASER_DISABLED);
3270 DrawLaser(0, DL_LASER_ENABLED);
3273 x = laser.damage[k1].x;
3274 y = laser.damage[k1].y;
3279 for (i = 0; i < 4; i++)
3281 if (laser.wall_mask & (1 << i))
3283 if (ReadPixel(drawto,
3284 SX + ELX * TILEX + 14 + (i % 2) * 2,
3285 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3287 if (ReadPixel(drawto,
3288 SX + ELX * TILEX + 31 * (i % 2),
3289 SY + ELY * TILEY + 14 + (i / 2) * 2) == pen_ray)
3296 for (i = 0; i < 4; i++)
3298 if (laser.wall_mask & (1 << i))
3300 if (ReadPixel(drawto,
3301 SX + ELX * TILEX + 31 * (i % 2),
3302 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3309 if (laser.num_beamers > 0 ||
3310 k1 < 1 || k2 < 4 || k3 < 4 ||
3311 ReadPixel(drawto, SX + ELX * TILEX + 14, SY + ELY * TILEY + 14)
3314 laser.num_edges = r;
3315 laser.num_damages = d;
3317 DrawLaser(0, DL_LASER_DISABLED);
3320 Feld[ELX][ELY] = element | laser.wall_mask;
3324 de = Feld[ELX][ELY];
3325 dm = laser.wall_mask;
3329 int x = ELX, y = ELY;
3330 int wall_mask = laser.wall_mask;
3333 DrawLaser(0, DL_LASER_ENABLED);
3335 PlaySoundStereo(SND_AMOEBE, ST(dx));
3337 Feld[x][y] = Feld[x][y] - EL_WALL_AMOEBA + EL_WALL_CHANGING;
3338 Store[x][y] = EL_WALL_AMOEBA;
3339 Store2[x][y] = wall_mask;
3345 DrawWallsAnimation_MM(dx, dy, de, 4, dm);
3347 DrawLaser(0, DL_LASER_ENABLED);
3349 PlaySoundStereo(SND_AMOEBE, ST(dx));
3351 for (i = 4; i >= 0; i--)
3353 DrawWallsAnimation_MM(dx, dy, de, i, dm);
3359 DrawLaser(0, DL_LASER_ENABLED);
3364 if ((element == EL_BLOCK_WOOD || element == EL_BLOCK_STONE) &&
3365 laser.stops_inside_element && CT > 1500)
3370 if (ABS(XS) > ABS(YS))
3377 for (i = 0; i < 4; i++)
3384 x = ELX + Step[k * 4].x;
3385 y = ELY + Step[k * 4].y;
3387 if (!IN_LEV_FIELD(x, y) || Feld[x][y] != EL_EMPTY)
3390 if (ObjHit(x, y, HIT_POS_CENTER | HIT_POS_EDGE | HIT_POS_BETWEEN))
3398 laser.overloaded = (element == EL_BLOCK_STONE);
3403 PlaySoundStereo(SND_BONG, ST(ELX));
3406 Feld[x][y] = element;
3408 DrawGraphic_MM(ELX, ELY, IMG_EMPTY);
3411 if (element == EL_BLOCK_STONE && Box[ELX][ELY])
3413 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
3414 DrawLaser(laser.num_edges - 1, DL_LASER_ENABLED);
3422 if (element == EL_FUEL_FULL && CT > 200)
3424 for (i = game_mm.energy_left; i <= MAX_LASER_ENERGY; i+=2)
3427 BlitBitmap(pix[PIX_DOOR], drawto,
3428 DOOR_GFX_PAGEX4 + XX_ENERGY,
3429 DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
3430 ENERGY_XSIZE, i, DX_ENERGY,
3431 DY_ENERGY + ENERGY_YSIZE - i);
3434 redraw_mask |= REDRAW_DOOR_1;
3440 game_mm.energy_left = MAX_LASER_ENERGY;
3441 Feld[ELX][ELY] = EL_FUEL_EMPTY;
3442 DrawField_MM(ELX, ELY);
3444 DrawLaser(0, DL_LASER_ENABLED);
3452 void GameActions_MM(byte action[MAX_PLAYERS], boolean warp_mode)
3455 ClickElement(0, 0, MB_NOT_PRESSED);
3457 GameActions_MM_Ext(action, warp_mode);
3463 int mx, my, ox, oy, nx, ny;
3467 if (++p >= game_mm.num_pacman)
3470 game_mm.pacman[p].dir--;
3472 for (l = 1; l < 5; l++)
3474 game_mm.pacman[p].dir++;
3476 if (game_mm.pacman[p].dir > 4)
3477 game_mm.pacman[p].dir = 1;
3479 if (game_mm.pacman[p].dir % 2)
3482 my = game_mm.pacman[p].dir - 2;
3487 mx = 3 - game_mm.pacman[p].dir;
3490 ox = game_mm.pacman[p].x;
3491 oy = game_mm.pacman[p].y;
3494 element = Feld[nx][ny];
3496 if (nx < 0 || nx > 15 || ny < 0 || ny > 11)
3499 if (!IS_EATABLE4PACMAN(element))
3502 if (ObjHit(nx, ny, HIT_POS_CENTER))
3505 Feld[ox][oy] = EL_EMPTY;
3507 EL_PACMAN_RIGHT - 1 +
3508 (game_mm.pacman[p].dir - 1 +
3509 (game_mm.pacman[p].dir % 2) * 2);
3511 game_mm.pacman[p].x = nx;
3512 game_mm.pacman[p].y = ny;
3514 DrawGraphic_MM(ox, oy, IMG_EMPTY);
3516 if (element != EL_EMPTY)
3518 int graphic = el2gfx(Feld[nx][ny]);
3523 getGraphicSource(graphic, 0, &bitmap, &src_x, &src_y);
3526 ox = SX + ox * TILEX;
3527 oy = SY + oy * TILEY;
3529 for (i = 1; i < 33; i += 2)
3530 BlitBitmap(bitmap, window,
3531 src_x, src_y, TILEX, TILEY,
3532 ox + i * mx, oy + i * my);
3533 Ct = Ct + Counter() - CT;
3536 DrawField_MM(nx, ny);
3539 if (!laser.fuse_off)
3541 DrawLaser(0, DL_LASER_ENABLED);
3543 if (ObjHit(nx, ny, HIT_POS_BETWEEN))
3545 AddDamagedField(nx, ny);
3547 laser.damage[laser.num_damages - 1].edge = 0;
3551 if (element == EL_BOMB)
3552 DeletePacMan(nx, ny);
3554 if (IS_WALL_AMOEBA(element) &&
3555 (LX + 2 * XS) / TILEX == nx &&
3556 (LY + 2 * YS) / TILEY == ny)
3569 boolean raise_level = FALSE;
3572 if (local_player->MovPos)
3575 local_player->LevelSolved = FALSE;
3578 if (game_mm.energy_left)
3580 if (setup.sound_loops)
3581 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3582 SND_CTRL_PLAY_LOOP);
3584 while (game_mm.energy_left > 0)
3586 if (!setup.sound_loops)
3587 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3590 if (game_mm.energy_left > 0 && !(game_mm.energy_left % 10))
3591 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3596 game_mm.energy_left--;
3597 if (game_mm.energy_left >= 0)
3600 BlitBitmap(pix[PIX_DOOR], drawto,
3601 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
3602 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
3603 DX_ENERGY, DY_ENERGY);
3605 redraw_mask |= REDRAW_DOOR_1;
3612 if (setup.sound_loops)
3613 StopSound(SND_SIRR);
3615 else if (native_mm_level.time == 0) /* level without time limit */
3617 if (setup.sound_loops)
3618 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3619 SND_CTRL_PLAY_LOOP);
3621 while (TimePlayed < 999)
3623 if (!setup.sound_loops)
3624 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3625 if (TimePlayed < 999 && !(TimePlayed % 10))
3626 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3627 if (TimePlayed < 900 && !(TimePlayed % 10))
3633 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
3640 if (setup.sound_loops)
3641 StopSound(SND_SIRR);
3648 CloseDoor(DOOR_CLOSE_1);
3650 Request("Level solved !", REQ_CONFIRM);
3652 if (level_nr == leveldir_current->handicap_level)
3654 leveldir_current->handicap_level++;
3655 SaveLevelSetup_SeriesInfo();
3658 if (level_editor_test_game)
3659 game_mm.score = -1; /* no highscore when playing from editor */
3660 else if (level_nr < leveldir_current->last_level)
3661 raise_level = TRUE; /* advance to next level */
3663 if ((hi_pos = NewHiScore_MM()) >= 0)
3665 game_status = HALLOFFAME;
3667 // DrawHallOfFame(hi_pos);
3674 game_status = MAINMENU;
3690 // LoadScore(level_nr);
3692 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
3693 game_mm.score < highscore[MAX_SCORE_ENTRIES - 1].Score)
3696 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3698 if (game_mm.score > highscore[k].Score)
3700 /* player has made it to the hall of fame */
3702 if (k < MAX_SCORE_ENTRIES - 1)
3704 int m = MAX_SCORE_ENTRIES - 1;
3707 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3708 if (!strcmp(setup.player_name, highscore[l].Name))
3710 if (m == k) /* player's new highscore overwrites his old one */
3714 for (l = m; l>k; l--)
3716 strcpy(highscore[l].Name, highscore[l - 1].Name);
3717 highscore[l].Score = highscore[l - 1].Score;
3724 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3725 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3726 highscore[k].Score = game_mm.score;
3733 else if (!strncmp(setup.player_name, highscore[k].Name,
3734 MAX_PLAYER_NAME_LEN))
3735 break; /* player already there with a higher score */
3740 // if (position >= 0)
3741 // SaveScore(level_nr);
3746 static void InitMovingField_MM(int x, int y, int direction)
3748 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3749 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3751 MovDir[x][y] = direction;
3752 MovDir[newx][newy] = direction;
3754 if (Feld[newx][newy] == EL_EMPTY)
3755 Feld[newx][newy] = EL_BLOCKED;
3758 static void Moving2Blocked_MM(int x, int y, int *goes_to_x, int *goes_to_y)
3760 int direction = MovDir[x][y];
3761 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3762 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3768 static void Blocked2Moving_MM(int x, int y,
3769 int *comes_from_x, int *comes_from_y)
3771 int oldx = x, oldy = y;
3772 int direction = MovDir[x][y];
3774 if (direction == MV_LEFT)
3776 else if (direction == MV_RIGHT)
3778 else if (direction == MV_UP)
3780 else if (direction == MV_DOWN)
3783 *comes_from_x = oldx;
3784 *comes_from_y = oldy;
3787 static int MovingOrBlocked2Element_MM(int x, int y)
3789 int element = Feld[x][y];
3791 if (element == EL_BLOCKED)
3795 Blocked2Moving_MM(x, y, &oldx, &oldy);
3797 return Feld[oldx][oldy];
3804 static void RemoveField(int x, int y)
3806 Feld[x][y] = EL_EMPTY;
3813 static void RemoveMovingField_MM(int x, int y)
3815 int oldx = x, oldy = y, newx = x, newy = y;
3817 if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
3820 if (IS_MOVING(x, y))
3822 Moving2Blocked_MM(x, y, &newx, &newy);
3823 if (Feld[newx][newy] != EL_BLOCKED)
3826 else if (Feld[x][y] == EL_BLOCKED)
3828 Blocked2Moving_MM(x, y, &oldx, &oldy);
3829 if (!IS_MOVING(oldx, oldy))
3833 Feld[oldx][oldy] = EL_EMPTY;
3834 Feld[newx][newy] = EL_EMPTY;
3835 MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
3836 MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
3838 DrawLevelField_MM(oldx, oldy);
3839 DrawLevelField_MM(newx, newy);
3842 void PlaySoundLevel(int x, int y, int sound_nr)
3844 int sx = SCREENX(x), sy = SCREENY(y);
3846 int silence_distance = 8;
3848 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
3849 (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
3852 if (!IN_LEV_FIELD(x, y) ||
3853 sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
3854 sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
3857 volume = SOUND_MAX_VOLUME;
3860 stereo = (sx - SCR_FIELDX/2) * 12;
3862 stereo = SOUND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
3863 if (stereo > SOUND_MAX_RIGHT)
3864 stereo = SOUND_MAX_RIGHT;
3865 if (stereo < SOUND_MAX_LEFT)
3866 stereo = SOUND_MAX_LEFT;
3869 if (!IN_SCR_FIELD(sx, sy))
3871 int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
3872 int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
3874 volume -= volume * (dx > dy ? dx : dy) / silence_distance;
3877 PlaySoundExt(sound_nr, volume, stereo, SND_CTRL_PLAY_SOUND);
3880 static void RaiseScore_MM(int value)
3882 game_mm.score += value;
3885 DrawText(DX_SCORE, DY_SCORE, int2str(game_mm.score, 4),
3890 void RaiseScoreElement_MM(int element)
3895 RaiseScore_MM(native_mm_level.score[SC_PACMAN]);
3899 RaiseScore_MM(native_mm_level.score[SC_KEY]);