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
72 #define AUTO_ROTATE_DELAY CLICK_DELAY_SHORT
73 #define INIT_GAME_ACTIONS_DELAY ONE_SECOND_DELAY
74 #define NUM_INIT_CYCLE_STEPS 16
76 /* forward declaration for internal use */
77 static int MovingOrBlocked2Element_MM(int, int);
78 static void Bang_MM(int, int);
79 static void RaiseScore_MM(int);
80 static void RemoveMovingField_MM(int, int);
81 static void InitMovingField_MM(int, int, int);
82 static void ContinueMoving_MM(int, int);
83 static void Moving2Blocked_MM(int, int, int *, int *);
86 static int get_element_angle(int element)
88 int element_phase = get_element_phase(element);
90 if (IS_MIRROR_FIXED(element) ||
91 IS_MCDUFFIN(element) ||
94 return 4 * element_phase;
99 static int get_opposite_angle(int angle)
101 int opposite_angle = angle + ANG_RAY_180;
103 /* make sure "opposite_angle" is in valid interval [0, 15] */
104 return (opposite_angle + 16) % 16;
107 static int get_mirrored_angle(int laser_angle, int mirror_angle)
109 int reflected_angle = 16 - laser_angle + mirror_angle;
111 /* make sure "reflected_angle" is in valid interval [0, 15] */
112 return (reflected_angle + 16) % 16;
115 static void InitMovDir_MM(int x, int y)
117 int element = Feld[x][y];
118 static int direction[3][4] =
120 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
121 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
122 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
127 case EL_PACMAN_RIGHT:
131 Feld[x][y] = EL_PACMAN;
132 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
140 static void InitField(int x, int y, boolean init_game)
142 int element = Feld[x][y];
147 Feld[x][y] = EL_EMPTY;
152 if (native_mm_level.auto_count_kettles)
153 game_mm.kettles_still_needed++;
156 case EL_LIGHTBULB_OFF:
157 game_mm.lights_still_needed++;
161 if (IS_MIRROR(element) ||
162 IS_BEAMER_OLD(element) ||
163 IS_BEAMER(element) ||
165 IS_POLAR_CROSS(element) ||
166 IS_DF_MIRROR(element) ||
167 IS_DF_MIRROR_AUTO(element) ||
168 IS_GRID_STEEL_AUTO(element) ||
169 IS_GRID_WOOD_AUTO(element) ||
170 IS_FIBRE_OPTIC(element))
172 if (IS_BEAMER_OLD(element))
174 Feld[x][y] = EL_BEAMER_BLUE_START + (element - EL_BEAMER_START);
175 element = Feld[x][y];
178 if (!IS_FIBRE_OPTIC(element))
180 static int steps_grid_auto = 0;
182 if (game_mm.num_cycle == 0) /* initialize cycle steps for grids */
183 steps_grid_auto = RND(16) * (RND(2) ? -1 : +1);
185 if (IS_GRID_STEEL_AUTO(element) ||
186 IS_GRID_WOOD_AUTO(element))
187 game_mm.cycle[game_mm.num_cycle].steps = steps_grid_auto;
189 game_mm.cycle[game_mm.num_cycle].steps = RND(16) * (RND(2) ? -1 : +1);
191 game_mm.cycle[game_mm.num_cycle].x = x;
192 game_mm.cycle[game_mm.num_cycle].y = y;
196 if (IS_BEAMER(element) || IS_FIBRE_OPTIC(element))
198 int beamer_nr = BEAMER_NR(element);
199 int nr = laser.beamer[beamer_nr][0].num;
201 laser.beamer[beamer_nr][nr].x = x;
202 laser.beamer[beamer_nr][nr].y = y;
203 laser.beamer[beamer_nr][nr].num = 1;
206 else if (IS_PACMAN(element))
210 else if (IS_MCDUFFIN(element) || IS_LASER(element))
212 laser.start_edge.x = x;
213 laser.start_edge.y = y;
214 laser.start_angle = get_element_angle(element);
221 static void InitCycleElements_RotateSingleStep()
225 if (game_mm.num_cycle == 0) /* no elements to cycle */
228 for (i = 0; i < game_mm.num_cycle; i++)
230 int x = game_mm.cycle[i].x;
231 int y = game_mm.cycle[i].y;
232 int step = SIGN(game_mm.cycle[i].steps);
233 int last_element = Feld[x][y];
234 int next_element = get_rotated_element(last_element, step);
236 if (!game_mm.cycle[i].steps)
239 Feld[x][y] = next_element;
242 game_mm.cycle[i].steps -= step;
246 static void InitLaser()
248 int start_element = Feld[laser.start_edge.x][laser.start_edge.y];
249 int step = (IS_LASER(start_element) ? 4 : 0);
251 LX = laser.start_edge.x * TILEX;
252 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
255 LX += (laser.start_angle == ANG_RAY_RIGHT ? 28 + step : 0 - step);
257 LY = laser.start_edge.y * TILEY;
258 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
259 LY += (laser.start_angle == ANG_RAY_DOWN ? 28 + step : 0 - step);
263 XS = 2 * Step[laser.start_angle].x;
264 YS = 2 * Step[laser.start_angle].y;
266 laser.current_angle = laser.start_angle;
268 laser.num_damages = 0;
270 laser.num_beamers = 0;
271 laser.beamer_edge[0] = 0;
273 AddLaserEdge(LX, LY); /* set laser starting edge */
275 pen_ray = GetPixelFromRGB(window,
276 native_mm_level.laser_red * 0xFF,
277 native_mm_level.laser_green * 0xFF,
278 native_mm_level.laser_blue * 0xFF);
281 void InitGameEngine_MM()
285 /* set global game control values */
286 game_mm.num_cycle = 0;
287 game_mm.num_pacman = 0;
290 game_mm.energy_left = 0; // later set to "native_mm_level.time"
291 game_mm.kettles_still_needed =
292 (native_mm_level.auto_count_kettles ? 0 : native_mm_level.kettles_needed);
293 game_mm.lights_still_needed = 0;
294 game_mm.num_keys = 0;
296 game_mm.level_solved = FALSE;
297 game_mm.game_over = FALSE;
298 game_mm.game_over_cause = 0;
300 game_mm.laser_overload_value = 0;
302 /* set global laser control values (must be set before "InitLaser()") */
303 laser.start_edge.x = 0;
304 laser.start_edge.y = 0;
305 laser.start_angle = 0;
307 for (i = 0; i < MAX_NUM_BEAMERS; i++)
308 laser.beamer[i][0].num = laser.beamer[i][1].num = 0;
310 laser.overloaded = FALSE;
311 laser.overload_value = 0;
312 laser.fuse_off = FALSE;
313 laser.fuse_x = laser.fuse_y = -1;
315 laser.dest_element = EL_EMPTY;
320 for (x = 0; x < lev_fieldx; x++)
322 for (y = 0; y < lev_fieldy; y++)
324 Feld[x][y] = Ur[x][y];
325 Hit[x][y] = Box[x][y] = 0;
327 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
328 Store[x][y] = Store2[x][y] = 0;
332 InitField(x, y, TRUE);
337 CloseDoor(DOOR_CLOSE_1);
343 void InitGameActions_MM()
345 int num_init_game_frames = INIT_GAME_ACTIONS_DELAY / GAME_FRAME_DELAY;
346 int cycle_steps_done = 0;
352 /* copy default game door content to main double buffer */
353 BlitBitmap(pix[PIX_DOOR], drawto,
354 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
358 DrawText(DX_LEVEL, DY_LEVEL,
359 int2str(level_nr, 2), FONT_TEXT_2);
360 DrawText(DX_KETTLES, DY_KETTLES,
361 int2str(game_mm.kettles_still_needed, 3), FONT_TEXT_2);
362 DrawText(DX_SCORE, DY_SCORE,
363 int2str(game_mm.score, 4), FONT_TEXT_2);
372 /* copy actual game door content to door double buffer for OpenDoor() */
373 BlitBitmap(drawto, pix[PIX_DB_DOOR],
374 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
378 OpenDoor(DOOR_OPEN_ALL);
381 for (i = 0; i <= num_init_game_frames; i++)
383 if (i == num_init_game_frames)
384 StopSound_MM(SND_MM_GAME_LEVELTIME_CHARGING);
385 else if (setup.sound_loops)
386 PlaySoundLoop_MM(SND_MM_GAME_LEVELTIME_CHARGING);
388 PlaySound_MM(SND_MM_GAME_LEVELTIME_CHARGING);
390 game_mm.energy_left = native_mm_level.time * i / num_init_game_frames;
392 UpdateAndDisplayGameControlValues();
394 while (cycle_steps_done < NUM_INIT_CYCLE_STEPS * i / num_init_game_frames)
396 InitCycleElements_RotateSingleStep();
406 if (setup.quick_doors)
412 if (setup.sound_music && num_bg_loops)
413 PlayMusic(level_nr % num_bg_loops);
419 void AddLaserEdge(int lx, int ly)
421 if (lx < -2 || ly < -2 || lx >= SXSIZE + 2 || ly >= SYSIZE + 2)
423 Error(ERR_WARN, "AddLaserEdge: out of bounds: %d, %d", lx, ly);
428 laser.edge[laser.num_edges].x = SX + 2 + lx;
429 laser.edge[laser.num_edges].y = SY + 2 + ly;
435 void AddDamagedField(int ex, int ey)
437 laser.damage[laser.num_damages].is_mirror = FALSE;
438 laser.damage[laser.num_damages].angle = laser.current_angle;
439 laser.damage[laser.num_damages].edge = laser.num_edges;
440 laser.damage[laser.num_damages].x = ex;
441 laser.damage[laser.num_damages].y = ey;
451 int last_x = laser.edge[laser.num_edges - 1].x - SX - 2;
452 int last_y = laser.edge[laser.num_edges - 1].y - SY - 2;
454 return ((x - last_x) * XS < 0 || (y - last_y) * YS < 0);
460 static int getMaskFromElement(int element)
462 if (IS_GRID(element))
463 return IMG_MM_MASK_GRID_1 + get_element_phase(element);
464 else if (IS_MCDUFFIN(element))
465 return IMG_MM_MASK_MCDUFFIN_RIGHT + get_element_phase(element);
466 else if (IS_RECTANGLE(element) || IS_DF_GRID(element))
467 return IMG_MM_MASK_RECTANGLE;
469 return IMG_MM_MASK_CIRCLE;
477 printf("ScanPixel: start scanning at (%d, %d) [%d, %d] [%d, %d]\n",
478 LX, LY, LX / TILEX, LY / TILEY, LX % TILEX, LY % TILEY);
481 /* follow laser beam until it hits something (at least the screen border) */
482 while (hit_mask == HIT_MASK_NO_HIT)
488 if (SX + LX < REAL_SX || SX + LX >= REAL_SX + FULL_SXSIZE ||
489 SY + LY < REAL_SY || SY + LY >= REAL_SY + FULL_SYSIZE)
491 printf("ScanPixel: touched screen border!\n");
497 for (i = 0; i < 4; i++)
499 int px = LX + (i % 2) * 2;
500 int py = LY + (i / 2) * 2;
503 int lx = (px + TILEX) / TILEX - 1; /* ...+TILEX...-1 to get correct */
504 int ly = (py + TILEY) / TILEY - 1; /* negative values! */
507 if (IN_LEV_FIELD(lx, ly))
509 int element = Feld[lx][ly];
511 if (element == EL_EMPTY || element == EL_EXPLODING_TRANSP)
515 else if (IS_WALL(element) || IS_WALL_CHANGING(element))
517 int pos = dy / MINI_TILEY * 2 + dx / MINI_TILEX;
519 pixel = ((element & (1 << pos)) ? 1 : 0);
523 int graphic_mask = getMaskFromElement(element);
528 getGraphicSource(graphic_mask, 0, &bitmap, &src_x, &src_y);
533 pixel = (ReadPixel(bitmap, mask_x, mask_y) ? 1 : 0);
538 pixel = (SX + px < REAL_SX || SX + px >= REAL_SX + FULL_SXSIZE ||
539 SY + py < REAL_SY || SY + py >= REAL_SY + FULL_SYSIZE);
542 if ((Sign[laser.current_angle] & (1 << i)) && pixel)
543 hit_mask |= (1 << i);
546 if (hit_mask == HIT_MASK_NO_HIT)
548 /* hit nothing -- go on with another step */
560 int end = 0, rf = laser.num_edges;
562 /* do not scan laser again after the game was lost for whatever reason */
563 if (game_mm.game_over)
566 laser.overloaded = FALSE;
567 laser.stops_inside_element = FALSE;
569 DrawLaser(0, DL_LASER_ENABLED);
572 printf("Start scanning with LX == %d, LY == %d, XS == %d, YS == %d\n",
580 if (laser.num_edges > MAX_LASER_LEN || laser.num_damages > MAX_LASER_LEN)
583 laser.overloaded = TRUE;
588 hit_mask = ScanPixel();
591 printf("Hit something at LX == %d, LY == %d, XS == %d, YS == %d\n",
595 /* hit something -- check out what it was */
596 ELX = (LX + XS) / TILEX;
597 ELY = (LY + YS) / TILEY;
600 printf("hit_mask (1) == '%x' (%d, %d) (%d, %d)\n",
601 hit_mask, LX, LY, ELX, ELY);
604 if (!IN_LEV_FIELD(ELX, ELY) || !IN_PIX_FIELD(LX, LY))
607 laser.dest_element = element;
612 if (hit_mask == (HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT))
614 /* we have hit the top-right and bottom-left element --
615 choose the bottom-left one */
616 /* !!! THIS CAN BE DONE MORE INTELLIGENTLY, FOR EXAMPLE, IF ONE
617 ELEMENT WAS STEEL AND THE OTHER ONE WAS ICE => ALWAYS CHOOSE
618 THE ICE AND MELT IT AWAY INSTEAD OF OVERLOADING LASER !!! */
619 ELX = (LX - 2) / TILEX;
620 ELY = (LY + 2) / TILEY;
623 if (hit_mask == (HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT))
625 /* we have hit the top-left and bottom-right element --
626 choose the top-left one */
627 /* !!! SEE ABOVE !!! */
628 ELX = (LX - 2) / TILEX;
629 ELY = (LY - 2) / TILEY;
633 printf("hit_mask (2) == '%x' (%d, %d) (%d, %d)\n",
634 hit_mask, LX, LY, ELX, ELY);
637 element = Feld[ELX][ELY];
638 laser.dest_element = element;
641 printf("Hit element %d at (%d, %d) [%d, %d] [%d, %d] [%d]\n",
644 LX % TILEX, LY % TILEY,
649 if (!IN_LEV_FIELD(ELX, ELY))
650 printf("WARNING! (1) %d, %d (%d)\n", ELX, ELY, element);
653 if (element == EL_EMPTY)
655 if (!HitOnlyAnEdge(element, hit_mask))
658 else if (element == EL_FUSE_ON)
660 if (HitPolarizer(element, hit_mask))
663 else if (IS_GRID(element) || IS_DF_GRID(element))
665 if (HitPolarizer(element, hit_mask))
668 else if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD ||
669 element == EL_GATE_STONE || element == EL_GATE_WOOD)
671 if (HitBlock(element, hit_mask))
678 else if (IS_MCDUFFIN(element))
680 if (HitLaserSource(element, hit_mask))
683 else if ((element >= EL_EXIT_CLOSED && element <= EL_EXIT_OPEN) ||
684 IS_RECEIVER(element))
686 if (HitLaserDestination(element, hit_mask))
689 else if (IS_WALL(element))
691 if (IS_WALL_STEEL(element) || IS_DF_WALL_STEEL(element))
693 if (HitReflectingWalls(element, hit_mask))
698 if (HitAbsorbingWalls(element, hit_mask))
704 if (HitElement(element, hit_mask))
709 DrawLaser(rf - 1, DL_LASER_ENABLED);
710 rf = laser.num_edges;
714 if (laser.dest_element != Feld[ELX][ELY])
716 printf("ALARM: laser.dest_element == %d, Feld[ELX][ELY] == %d\n",
717 laser.dest_element, Feld[ELX][ELY]);
721 if (!end && !laser.stops_inside_element && !StepBehind())
724 printf("ScanLaser: Go one step back\n");
730 AddLaserEdge(LX, LY);
734 DrawLaser(rf - 1, DL_LASER_ENABLED);
739 if (!IN_LEV_FIELD(ELX, ELY))
740 printf("WARNING! (2) %d, %d\n", ELX, ELY);
744 void DrawLaserExt(int start_edge, int num_edges, int mode)
750 printf("DrawLaserExt: start_edge, num_edges, mode == %d, %d, %d\n",
751 start_edge, num_edges, mode);
756 Error(ERR_WARN, "DrawLaserExt: start_edge < 0");
763 Error(ERR_WARN, "DrawLaserExt: num_edges < 0");
769 if (mode == DL_LASER_DISABLED)
771 printf("DrawLaser: Delete laser from edge %d\n", start_edge);
775 /* now draw the laser to the backbuffer and (if enabled) to the screen */
776 DrawLines(drawto, &laser.edge[start_edge], num_edges,
777 (mode == DL_LASER_ENABLED ? pen_ray : pen_bg));
779 redraw_mask |= REDRAW_FIELD;
781 if (mode == DL_LASER_ENABLED)
784 /* after the laser was deleted, the "damaged" graphics must be restored */
785 if (laser.num_damages)
787 int damage_start = 0;
790 /* determine the starting edge, from which graphics need to be restored */
793 for (i = 0; i < laser.num_damages; i++)
795 if (laser.damage[i].edge == start_edge + 1)
804 /* restore graphics from this starting edge to the end of damage list */
805 for (i = damage_start; i < laser.num_damages; i++)
807 int lx = laser.damage[i].x;
808 int ly = laser.damage[i].y;
809 int element = Feld[lx][ly];
811 if (Hit[lx][ly] == laser.damage[i].edge)
812 if (!((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
815 if (Box[lx][ly] == laser.damage[i].edge)
818 if (IS_DRAWABLE(element))
819 DrawField_MM(lx, ly);
822 elx = laser.damage[damage_start].x;
823 ely = laser.damage[damage_start].y;
824 element = Feld[elx][ely];
827 if (IS_BEAMER(element))
831 for (i = 0; i < laser.num_beamers; i++)
832 printf("-> %d\n", laser.beamer_edge[i]);
833 printf("DrawLaserExt: IS_BEAMER: [%d]: Hit[%d][%d] == %d [%d]\n",
834 mode, elx, ely, Hit[elx][ely], start_edge);
835 printf("DrawLaserExt: IS_BEAMER: %d / %d\n",
836 get_element_angle(element), laser.damage[damage_start].angle);
840 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
841 laser.num_beamers > 0 &&
842 start_edge == laser.beamer_edge[laser.num_beamers - 1])
844 /* element is outgoing beamer */
845 laser.num_damages = damage_start + 1;
847 if (IS_BEAMER(element))
848 laser.current_angle = get_element_angle(element);
852 /* element is incoming beamer or other element */
853 laser.num_damages = damage_start;
854 laser.current_angle = laser.damage[laser.num_damages].angle;
859 /* no damages but McDuffin himself (who needs to be redrawn anyway) */
861 elx = laser.start_edge.x;
862 ely = laser.start_edge.y;
863 element = Feld[elx][ely];
866 laser.num_edges = start_edge + 1;
868 laser.current_angle = laser.start_angle;
870 LX = laser.edge[start_edge].x - (SX + 2);
871 LY = laser.edge[start_edge].y - (SY + 2);
872 XS = 2 * Step[laser.current_angle].x;
873 YS = 2 * Step[laser.current_angle].y;
876 printf("DrawLaser: Set (LX, LY) to (%d, %d) [%d]\n",
882 if (IS_BEAMER(element) ||
883 IS_FIBRE_OPTIC(element) ||
884 IS_PACMAN(element) ||
886 IS_POLAR_CROSS(element) ||
887 element == EL_FUSE_ON)
892 printf("element == %d\n", element);
895 if (IS_22_5_ANGLE(laser.current_angle)) /* neither 90° nor 45° angle */
896 step_size = ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) ? 4 : 3);
900 if (IS_POLAR(element) || IS_POLAR_CROSS(element) ||
901 ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
902 (laser.num_beamers == 0 ||
903 start_edge != laser.beamer_edge[laser.num_beamers - 1])))
905 /* element is incoming beamer or other element */
906 step_size = -step_size;
911 if (IS_BEAMER(element))
913 printf("start_edge == %d, laser.beamer_edge == %d\n",
914 start_edge, laser.beamer_edge);
918 LX += step_size * XS;
919 LY += step_size * YS;
921 else if (element != EL_EMPTY)
930 printf("DrawLaser: Finally: (LX, LY) to (%d, %d) [%d]\n",
935 void DrawLaser(int start_edge, int mode)
937 if (laser.num_edges - start_edge < 0)
939 Error(ERR_WARN, "DrawLaser: laser.num_edges - start_edge < 0");
944 /* check if laser is interrupted by beamer element */
945 if (laser.num_beamers > 0 &&
946 start_edge < laser.beamer_edge[laser.num_beamers - 1])
948 if (mode == DL_LASER_ENABLED)
951 int tmp_start_edge = start_edge;
953 /* draw laser segments forward from the start to the last beamer */
954 for (i = 0; i < laser.num_beamers; i++)
956 int tmp_num_edges = laser.beamer_edge[i] - tmp_start_edge;
958 if (tmp_num_edges <= 0)
962 printf("DrawLaser: DL_LASER_ENABLED: i==%d: %d, %d\n",
963 i, laser.beamer_edge[i], tmp_start_edge);
966 DrawLaserExt(tmp_start_edge, tmp_num_edges, DL_LASER_ENABLED);
968 tmp_start_edge = laser.beamer_edge[i];
971 /* draw last segment from last beamer to the end */
972 DrawLaserExt(tmp_start_edge, laser.num_edges - tmp_start_edge,
978 int last_num_edges = laser.num_edges;
979 int num_beamers = laser.num_beamers;
981 /* delete laser segments backward from the end to the first beamer */
982 for (i = num_beamers-1; i >= 0; i--)
984 int tmp_num_edges = last_num_edges - laser.beamer_edge[i];
986 if (laser.beamer_edge[i] - start_edge <= 0)
989 DrawLaserExt(laser.beamer_edge[i], tmp_num_edges, DL_LASER_DISABLED);
991 last_num_edges = laser.beamer_edge[i];
996 if (last_num_edges - start_edge <= 0)
997 printf("DrawLaser: DL_LASER_DISABLED: %d, %d\n",
998 last_num_edges, start_edge);
1001 /* delete first segment from start to the first beamer */
1002 DrawLaserExt(start_edge, last_num_edges - start_edge, DL_LASER_DISABLED);
1007 DrawLaserExt(start_edge, laser.num_edges - start_edge, mode);
1011 boolean HitElement(int element, int hit_mask)
1013 if (HitOnlyAnEdge(element, hit_mask))
1016 if (IS_MOVING(ELX, ELY) || IS_BLOCKED(ELX, ELY))
1017 element = MovingOrBlocked2Element_MM(ELX, ELY);
1020 printf("HitElement (1): element == %d\n", element);
1024 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1025 printf("HitElement (%d): EXACT MATCH @ (%d, %d)\n", element, ELX, ELY);
1027 printf("HitElement (%d): FUZZY MATCH @ (%d, %d)\n", element, ELX, ELY);
1030 AddDamagedField(ELX, ELY);
1032 /* this is more precise: check if laser would go through the center */
1033 if ((ELX * TILEX + 14 - LX) * YS != (ELY * TILEY + 14 - LY) * XS)
1035 /* skip the whole element before continuing the scan */
1041 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1043 if (LX/TILEX > ELX || LY/TILEY > ELY)
1045 /* skipping scan positions to the right and down skips one scan
1046 position too much, because this is only the top left scan position
1047 of totally four scan positions (plus one to the right, one to the
1048 bottom and one to the bottom right) */
1058 printf("HitElement (2): element == %d\n", element);
1061 if (LX + 5 * XS < 0 ||
1071 printf("HitElement (3): element == %d\n", element);
1074 if (IS_POLAR(element) &&
1075 ((element - EL_POLAR_START) % 2 ||
1076 (element - EL_POLAR_START) / 2 != laser.current_angle % 8))
1078 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1080 laser.num_damages--;
1085 if (IS_POLAR_CROSS(element) &&
1086 (element - EL_POLAR_CROSS_START) != laser.current_angle % 4)
1088 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1090 laser.num_damages--;
1095 if (!IS_BEAMER(element) &&
1096 !IS_FIBRE_OPTIC(element) &&
1097 !IS_GRID_WOOD(element) &&
1098 element != EL_FUEL_EMPTY)
1101 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1102 printf("EXACT MATCH @ (%d, %d)\n", ELX, ELY);
1104 printf("FUZZY MATCH @ (%d, %d)\n", ELX, ELY);
1107 LX = ELX * TILEX + 14;
1108 LY = ELY * TILEY + 14;
1110 AddLaserEdge(LX, LY);
1113 if (IS_MIRROR(element) ||
1114 IS_MIRROR_FIXED(element) ||
1115 IS_POLAR(element) ||
1116 IS_POLAR_CROSS(element) ||
1117 IS_DF_MIRROR(element) ||
1118 IS_DF_MIRROR_AUTO(element) ||
1119 element == EL_PRISM ||
1120 element == EL_REFRACTOR)
1122 int current_angle = laser.current_angle;
1125 laser.num_damages--;
1127 AddDamagedField(ELX, ELY);
1129 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1132 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1134 if (IS_MIRROR(element) ||
1135 IS_MIRROR_FIXED(element) ||
1136 IS_DF_MIRROR(element) ||
1137 IS_DF_MIRROR_AUTO(element))
1138 laser.current_angle = get_mirrored_angle(laser.current_angle,
1139 get_element_angle(element));
1141 if (element == EL_PRISM || element == EL_REFRACTOR)
1142 laser.current_angle = RND(16);
1144 XS = 2 * Step[laser.current_angle].x;
1145 YS = 2 * Step[laser.current_angle].y;
1147 if (!IS_22_5_ANGLE(laser.current_angle)) /* 90° or 45° angle */
1152 LX += step_size * XS;
1153 LY += step_size * YS;
1156 /* draw sparkles on mirror */
1157 if ((IS_MIRROR(element) || IS_MIRROR_FIXED(element)) &&
1158 current_angle != laser.current_angle)
1160 MoveSprite(vp, &Pfeil[2], 4 + 16 * ELX, 5 + 16 * ELY + 1);
1164 if ((!IS_POLAR(element) && !IS_POLAR_CROSS(element)) &&
1165 current_angle != laser.current_angle)
1166 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1169 (get_opposite_angle(laser.current_angle) ==
1170 laser.damage[laser.num_damages - 1].angle ? TRUE : FALSE);
1172 return (laser.overloaded ? TRUE : FALSE);
1175 if (element == EL_FUEL_FULL)
1177 laser.stops_inside_element = TRUE;
1182 if (element == EL_BOMB || element == EL_MINE)
1184 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1186 if (element == EL_MINE)
1187 laser.overloaded = TRUE;
1190 if (element == EL_KETTLE ||
1191 element == EL_CELL ||
1192 element == EL_KEY ||
1193 element == EL_LIGHTBALL ||
1194 element == EL_PACMAN ||
1197 if (!IS_PACMAN(element))
1200 if (element == EL_PACMAN)
1203 if (element == EL_KETTLE || element == EL_CELL)
1205 if (game_mm.kettles_still_needed > 0)
1206 game_mm.kettles_still_needed--;
1210 if (game_mm.kettles_still_needed == 0)
1212 int exit_element = (element == EL_KETTLE ? EL_EXIT_OPEN : EL_RECEIVER);
1214 static int xy[4][2] =
1222 PlayLevelSound_MM(ELX, ELY, exit_element, MM_ACTION_OPENING);
1224 for (y = 0; y < lev_fieldy; y++)
1226 for (x = 0; x < lev_fieldx; x++)
1228 /* initiate opening animation of exit door */
1229 if (Feld[x][y] == EL_EXIT_CLOSED)
1230 Feld[x][y] = EL_EXIT_OPENING;
1232 /* remove field that blocks receiver */
1233 if (IS_RECEIVER(Feld[x][y]))
1235 int phase = Feld[x][y] - EL_RECEIVER_START;
1236 int blocking_x, blocking_y;
1238 blocking_x = x + xy[phase][0];
1239 blocking_y = y + xy[phase][1];
1241 if (IN_LEV_FIELD(blocking_x, blocking_y))
1243 Feld[blocking_x][blocking_y] = EL_EMPTY;
1245 DrawField_MM(blocking_x, blocking_y);
1251 DrawLaser(0, DL_LASER_ENABLED);
1254 else if (element == EL_KEY)
1258 else if (element == EL_LIGHTBALL)
1262 else if (IS_PACMAN(element))
1264 DeletePacMan(ELX, ELY);
1271 if (element == EL_LIGHTBULB_OFF || element == EL_LIGHTBULB_ON)
1273 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1275 DrawLaser(0, DL_LASER_ENABLED);
1277 if (Feld[ELX][ELY] == EL_LIGHTBULB_OFF)
1279 Feld[ELX][ELY] = EL_LIGHTBULB_ON;
1280 game_mm.lights_still_needed--;
1284 Feld[ELX][ELY] = EL_LIGHTBULB_OFF;
1285 game_mm.lights_still_needed++;
1288 DrawField_MM(ELX, ELY);
1289 DrawLaser(0, DL_LASER_ENABLED);
1294 laser.stops_inside_element = TRUE;
1300 printf("HitElement (4): element == %d\n", element);
1303 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1304 laser.num_beamers < MAX_NUM_BEAMERS &&
1305 laser.beamer[BEAMER_NR(element)][1].num)
1307 int beamer_angle = get_element_angle(element);
1308 int beamer_nr = BEAMER_NR(element);
1312 printf("HitElement (BEAMER): element == %d\n", element);
1315 laser.num_damages--;
1317 if (IS_FIBRE_OPTIC(element) ||
1318 laser.current_angle == get_opposite_angle(beamer_angle))
1322 LX = ELX * TILEX + 14;
1323 LY = ELY * TILEY + 14;
1325 AddLaserEdge(LX, LY);
1326 AddDamagedField(ELX, ELY);
1328 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1331 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1333 pos = (ELX == laser.beamer[beamer_nr][0].x &&
1334 ELY == laser.beamer[beamer_nr][0].y ? 1 : 0);
1335 ELX = laser.beamer[beamer_nr][pos].x;
1336 ELY = laser.beamer[beamer_nr][pos].y;
1337 LX = ELX * TILEX + 14;
1338 LY = ELY * TILEY + 14;
1340 if (IS_BEAMER(element))
1342 laser.current_angle = get_element_angle(Feld[ELX][ELY]);
1343 XS = 2 * Step[laser.current_angle].x;
1344 YS = 2 * Step[laser.current_angle].y;
1347 laser.beamer_edge[laser.num_beamers] = laser.num_edges;
1349 AddLaserEdge(LX, LY);
1350 AddDamagedField(ELX, ELY);
1352 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1355 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1357 if (laser.current_angle == (laser.current_angle >> 1) << 1)
1362 LX += step_size * XS;
1363 LY += step_size * YS;
1365 laser.num_beamers++;
1374 boolean HitOnlyAnEdge(int element, int hit_mask)
1376 /* check if the laser hit only the edge of an element and, if so, go on */
1379 printf("LX, LY, hit_mask == %d, %d, %d\n", LX, LY, hit_mask);
1382 if ((hit_mask == HIT_MASK_TOPLEFT ||
1383 hit_mask == HIT_MASK_TOPRIGHT ||
1384 hit_mask == HIT_MASK_BOTTOMLEFT ||
1385 hit_mask == HIT_MASK_BOTTOMRIGHT) &&
1386 laser.current_angle % 4) /* angle is not 90° */
1390 if (hit_mask == HIT_MASK_TOPLEFT)
1395 else if (hit_mask == HIT_MASK_TOPRIGHT)
1400 else if (hit_mask == HIT_MASK_BOTTOMLEFT)
1405 else /* (hit_mask == HIT_MASK_BOTTOMRIGHT) */
1411 AddDamagedField((LX + 2 * dx) / TILEX, (LY + 2 * dy) / TILEY);
1417 printf("[HitOnlyAnEdge() == TRUE]\n");
1424 printf("[HitOnlyAnEdge() == FALSE]\n");
1430 boolean HitPolarizer(int element, int hit_mask)
1432 if (HitOnlyAnEdge(element, hit_mask))
1435 if (IS_DF_GRID(element))
1437 int grid_angle = get_element_angle(element);
1440 printf("HitPolarizer: angle: grid == %d, laser == %d\n",
1441 grid_angle, laser.current_angle);
1444 AddLaserEdge(LX, LY);
1445 AddDamagedField(ELX, ELY);
1448 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1450 if (laser.current_angle == grid_angle ||
1451 laser.current_angle == get_opposite_angle(grid_angle))
1453 /* skip the whole element before continuing the scan */
1459 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1461 if (LX/TILEX > ELX || LY/TILEY > ELY)
1463 /* skipping scan positions to the right and down skips one scan
1464 position too much, because this is only the top left scan position
1465 of totally four scan positions (plus one to the right, one to the
1466 bottom and one to the bottom right) */
1472 AddLaserEdge(LX, LY);
1478 printf("HitPolarizer: LX, LY == %d, %d [%d, %d] [%d, %d]\n",
1480 LX / TILEX, LY / TILEY,
1481 LX % TILEX, LY % TILEY);
1486 else if (IS_GRID_STEEL_FIXED(element) || IS_GRID_STEEL_AUTO(element))
1488 return HitReflectingWalls(element, hit_mask);
1492 return HitAbsorbingWalls(element, hit_mask);
1495 else if (IS_GRID_STEEL(element))
1497 return HitReflectingWalls(element, hit_mask);
1499 else /* IS_GRID_WOOD */
1501 return HitAbsorbingWalls(element, hit_mask);
1507 boolean HitBlock(int element, int hit_mask)
1509 boolean check = FALSE;
1511 if ((element == EL_GATE_STONE || element == EL_GATE_WOOD) &&
1512 game_mm.num_keys == 0)
1515 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1518 int ex = ELX * TILEX + 14;
1519 int ey = ELY * TILEY + 14;
1523 for (i = 1; i < 32; i++)
1528 if ((x == ex || x == ex + 1) && (y == ey || y == ey + 1))
1533 if (check && (element == EL_BLOCK_WOOD || element == EL_GATE_WOOD))
1534 return HitAbsorbingWalls(element, hit_mask);
1538 AddLaserEdge(LX - XS, LY - YS);
1539 AddDamagedField(ELX, ELY);
1542 Box[ELX][ELY] = laser.num_edges;
1544 return HitReflectingWalls(element, hit_mask);
1547 if (element == EL_GATE_STONE || element == EL_GATE_WOOD)
1549 int xs = XS / 2, ys = YS / 2;
1550 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1551 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1553 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1554 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1556 laser.overloaded = (element == EL_GATE_STONE);
1561 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1562 (hit_mask == HIT_MASK_TOP ||
1563 hit_mask == HIT_MASK_LEFT ||
1564 hit_mask == HIT_MASK_RIGHT ||
1565 hit_mask == HIT_MASK_BOTTOM))
1566 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1567 hit_mask == HIT_MASK_BOTTOM),
1568 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1569 hit_mask == HIT_MASK_RIGHT));
1570 AddLaserEdge(LX, LY);
1576 if (element == EL_GATE_STONE && Box[ELX][ELY])
1578 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
1590 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1592 int xs = XS / 2, ys = YS / 2;
1593 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1594 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1596 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1597 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1599 laser.overloaded = (element == EL_BLOCK_STONE);
1604 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1605 (hit_mask == HIT_MASK_TOP ||
1606 hit_mask == HIT_MASK_LEFT ||
1607 hit_mask == HIT_MASK_RIGHT ||
1608 hit_mask == HIT_MASK_BOTTOM))
1609 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1610 hit_mask == HIT_MASK_BOTTOM),
1611 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1612 hit_mask == HIT_MASK_RIGHT));
1613 AddDamagedField(ELX, ELY);
1615 LX = ELX * TILEX + 14;
1616 LY = ELY * TILEY + 14;
1618 AddLaserEdge(LX, LY);
1620 laser.stops_inside_element = TRUE;
1628 boolean HitLaserSource(int element, int hit_mask)
1630 if (HitOnlyAnEdge(element, hit_mask))
1633 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1635 laser.overloaded = TRUE;
1640 boolean HitLaserDestination(int element, int hit_mask)
1642 if (HitOnlyAnEdge(element, hit_mask))
1645 if (element != EL_EXIT_OPEN &&
1646 !(IS_RECEIVER(element) &&
1647 game_mm.kettles_still_needed == 0 &&
1648 laser.current_angle == get_opposite_angle(get_element_angle(element))))
1650 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1655 if (IS_RECEIVER(element) ||
1656 (IS_22_5_ANGLE(laser.current_angle) &&
1657 (ELX != (LX + 6 * XS) / TILEX ||
1658 ELY != (LY + 6 * YS) / TILEY ||
1667 LX = ELX * TILEX + 14;
1668 LY = ELY * TILEY + 14;
1670 laser.stops_inside_element = TRUE;
1673 AddLaserEdge(LX, LY);
1674 AddDamagedField(ELX, ELY);
1676 if (game_mm.lights_still_needed == 0)
1677 game_mm.level_solved = TRUE;
1682 boolean HitReflectingWalls(int element, int hit_mask)
1684 /* check if laser hits side of a wall with an angle that is not 90° */
1685 if (!IS_90_ANGLE(laser.current_angle) && (hit_mask == HIT_MASK_TOP ||
1686 hit_mask == HIT_MASK_LEFT ||
1687 hit_mask == HIT_MASK_RIGHT ||
1688 hit_mask == HIT_MASK_BOTTOM))
1690 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1695 if (!IS_DF_GRID(element))
1696 AddLaserEdge(LX, LY);
1698 /* check if laser hits wall with an angle of 45° */
1699 if (!IS_22_5_ANGLE(laser.current_angle))
1701 if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1704 laser.current_angle = get_mirrored_angle(laser.current_angle,
1707 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1710 laser.current_angle = get_mirrored_angle(laser.current_angle,
1714 AddLaserEdge(LX, LY);
1716 XS = 2 * Step[laser.current_angle].x;
1717 YS = 2 * Step[laser.current_angle].y;
1721 else if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1723 laser.current_angle = get_mirrored_angle(laser.current_angle,
1728 if (!IS_DF_GRID(element))
1729 AddLaserEdge(LX, LY);
1734 if (!IS_DF_GRID(element))
1735 AddLaserEdge(LX, LY + YS / 2);
1738 if (!IS_DF_GRID(element))
1739 AddLaserEdge(LX, LY);
1742 YS = 2 * Step[laser.current_angle].y;
1746 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1748 laser.current_angle = get_mirrored_angle(laser.current_angle,
1753 if (!IS_DF_GRID(element))
1754 AddLaserEdge(LX, LY);
1759 if (!IS_DF_GRID(element))
1760 AddLaserEdge(LX + XS / 2, LY);
1763 if (!IS_DF_GRID(element))
1764 AddLaserEdge(LX, LY);
1767 XS = 2 * Step[laser.current_angle].x;
1773 /* reflection at the edge of reflecting DF style wall */
1774 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1776 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1777 hit_mask == HIT_MASK_TOPRIGHT) ||
1778 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1779 hit_mask == HIT_MASK_TOPLEFT) ||
1780 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1781 hit_mask == HIT_MASK_BOTTOMLEFT) ||
1782 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1783 hit_mask == HIT_MASK_BOTTOMRIGHT))
1786 (hit_mask == HIT_MASK_TOPRIGHT || hit_mask == HIT_MASK_BOTTOMLEFT ?
1787 ANG_MIRROR_135 : ANG_MIRROR_45);
1789 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1791 AddDamagedField(ELX, ELY);
1792 AddLaserEdge(LX, LY);
1794 laser.current_angle = get_mirrored_angle(laser.current_angle,
1802 AddLaserEdge(LX, LY);
1808 /* reflection inside an edge of reflecting DF style wall */
1809 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1811 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1812 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT)) ||
1813 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1814 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMRIGHT)) ||
1815 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1816 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT)) ||
1817 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1818 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPLEFT)))
1821 (hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT) ||
1822 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT) ?
1823 ANG_MIRROR_135 : ANG_MIRROR_45);
1825 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1828 AddDamagedField(ELX, ELY);
1831 AddLaserEdge(LX - XS, LY - YS);
1832 AddLaserEdge(LX - XS + (ABS(XS) == 4 ? XS/2 : 0),
1833 LY - YS + (ABS(YS) == 4 ? YS/2 : 0));
1835 laser.current_angle = get_mirrored_angle(laser.current_angle,
1843 AddLaserEdge(LX, LY);
1849 /* check if laser hits DF style wall with an angle of 90° */
1850 if (IS_DF_WALL(element) && IS_90_ANGLE(laser.current_angle))
1852 if ((IS_HORIZ_ANGLE(laser.current_angle) &&
1853 (!(hit_mask & HIT_MASK_TOP) || !(hit_mask & HIT_MASK_BOTTOM))) ||
1854 (IS_VERT_ANGLE(laser.current_angle) &&
1855 (!(hit_mask & HIT_MASK_LEFT) || !(hit_mask & HIT_MASK_RIGHT))))
1857 static int last_LX = 0, last_LY = 0, last_hit_mask = 0;
1859 /* laser at last step touched nothing or the same side of the wall */
1860 if (LX != last_LX || LY != last_LY || hit_mask == last_hit_mask)
1862 AddDamagedField(ELX, ELY);
1869 last_hit_mask = hit_mask;
1876 if (!HitOnlyAnEdge(element, hit_mask))
1878 laser.overloaded = TRUE;
1886 boolean HitAbsorbingWalls(int element, int hit_mask)
1888 if (HitOnlyAnEdge(element, hit_mask))
1892 (hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT))
1894 AddLaserEdge(LX - XS, LY - YS);
1901 (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM))
1903 AddLaserEdge(LX - XS, LY - YS);
1909 if (IS_WALL_WOOD(element) ||
1910 IS_DF_WALL_WOOD(element) ||
1911 IS_GRID_WOOD(element) ||
1912 IS_GRID_WOOD_FIXED(element) ||
1913 IS_GRID_WOOD_AUTO(element) ||
1914 element == EL_FUSE_ON ||
1915 element == EL_BLOCK_WOOD ||
1916 element == EL_GATE_WOOD)
1918 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1923 if (IS_WALL_ICE(element))
1927 mask = (LX + XS) / MINI_TILEX - ELX * 2 + 1; /* Quadrant (horizontal) */
1928 mask <<= (((LY + YS) / MINI_TILEY - ELY * 2) > 0) * 2; /* || (vertical) */
1930 /* check if laser hits wall with an angle of 90° */
1931 if (IS_90_ANGLE(laser.current_angle))
1932 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1934 if (mask == 1 || mask == 2 || mask == 4 || mask == 8)
1938 for (i = 0; i < 4; i++)
1940 if (mask == (1 << i) && (XS > 0) == (i % 2) && (YS > 0) == (i / 2))
1941 mask = 15 - (8 >> i);
1942 else if (ABS(XS) == 4 &&
1944 (XS > 0) == (i % 2) &&
1945 (YS < 0) == (i / 2))
1946 mask = 3 + (i / 2) * 9;
1947 else if (ABS(YS) == 4 &&
1949 (XS < 0) == (i % 2) &&
1950 (YS > 0) == (i / 2))
1951 mask = 5 + (i % 2) * 5;
1955 laser.wall_mask = mask;
1957 else if (IS_WALL_AMOEBA(element))
1959 int elx = (LX - 2 * XS) / TILEX;
1960 int ely = (LY - 2 * YS) / TILEY;
1961 int element2 = Feld[elx][ely];
1964 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element2))
1966 laser.dest_element = EL_EMPTY;
1974 mask = (LX - 2 * XS) / 16 - ELX * 2 + 1;
1975 mask <<= ((LY - 2 * YS) / 16 - ELY * 2) * 2;
1977 if (IS_90_ANGLE(laser.current_angle))
1978 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1980 laser.dest_element = element2 | EL_WALL_AMOEBA;
1982 laser.wall_mask = mask;
1988 void OpenExit(int x, int y)
1992 if (!MovDelay[x][y]) /* next animation frame */
1993 MovDelay[x][y] = 4 * delay;
1995 if (MovDelay[x][y]) /* wait some time before next frame */
2000 phase = MovDelay[x][y] / delay;
2002 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2003 DrawGraphicAnimation_MM(x, y, IMG_MM_EXIT_OPENING, 3 - phase);
2005 if (!MovDelay[x][y])
2007 Feld[x][y] = EL_EXIT_OPEN;
2013 void OpenSurpriseBall(int x, int y)
2017 if (!MovDelay[x][y]) /* next animation frame */
2018 MovDelay[x][y] = 50 * delay;
2020 if (MovDelay[x][y]) /* wait some time before next frame */
2024 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2027 int graphic = el2gfx(Store[x][y]);
2029 int dx = RND(26), dy = RND(26);
2031 getGraphicSource(graphic, 0, &bitmap, &gx, &gy);
2033 BlitBitmap(bitmap, drawto, gx + dx, gy + dy, 6, 6,
2034 SX + x * TILEX + dx, SY + y * TILEY + dy);
2036 MarkTileDirty(x, y);
2039 if (!MovDelay[x][y])
2041 Feld[x][y] = Store[x][y];
2050 void MeltIce(int x, int y)
2055 if (!MovDelay[x][y]) /* next animation frame */
2056 MovDelay[x][y] = frames * delay;
2058 if (MovDelay[x][y]) /* wait some time before next frame */
2061 int wall_mask = Store2[x][y];
2062 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_ICE;
2065 phase = frames - MovDelay[x][y] / delay - 1;
2067 if (!MovDelay[x][y])
2071 Feld[x][y] = real_element & (wall_mask ^ 0xFF);
2072 Store[x][y] = Store2[x][y] = 0;
2074 DrawWalls_MM(x, y, Feld[x][y]);
2076 if (Feld[x][y] == EL_WALL_ICE)
2077 Feld[x][y] = EL_EMPTY;
2079 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
2080 if (laser.damage[i].is_mirror)
2084 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
2086 DrawLaser(0, DL_LASER_DISABLED);
2090 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2092 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2094 laser.redraw = TRUE;
2099 void GrowAmoeba(int x, int y)
2104 if (!MovDelay[x][y]) /* next animation frame */
2105 MovDelay[x][y] = frames * delay;
2107 if (MovDelay[x][y]) /* wait some time before next frame */
2110 int wall_mask = Store2[x][y];
2111 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_AMOEBA;
2114 phase = MovDelay[x][y] / delay;
2116 if (!MovDelay[x][y])
2118 Feld[x][y] = real_element;
2119 Store[x][y] = Store2[x][y] = 0;
2121 DrawWalls_MM(x, y, Feld[x][y]);
2122 DrawLaser(0, DL_LASER_ENABLED);
2124 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2126 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2131 static void Explode_MM(int x, int y, int phase, int mode)
2133 int num_phase = 9, delay = 2;
2134 int last_phase = num_phase * delay;
2135 int half_phase = (num_phase / 2) * delay;
2137 laser.redraw = TRUE;
2139 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2141 int center_element = Feld[x][y];
2143 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2145 /* put moving element to center field (and let it explode there) */
2146 center_element = MovingOrBlocked2Element_MM(x, y);
2147 RemoveMovingField_MM(x, y);
2149 Feld[x][y] = center_element;
2152 if (center_element == EL_BOMB || IS_MCDUFFIN(center_element))
2153 Store[x][y] = center_element;
2155 Store[x][y] = EL_EMPTY;
2157 Store2[x][y] = mode;
2158 Feld[x][y] = EL_EXPLODING_OPAQUE;
2159 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2165 Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
2167 if (phase == half_phase)
2169 Feld[x][y] = EL_EXPLODING_TRANSP;
2171 if (x == ELX && y == ELY)
2175 if (phase == last_phase)
2177 if (Store[x][y] == EL_BOMB)
2179 laser.num_damages--;
2180 DrawLaser(0, DL_LASER_DISABLED);
2181 laser.num_edges = 0;
2183 Bang_MM(laser.start_edge.x, laser.start_edge.y);
2184 Store[x][y] = EL_EMPTY;
2186 else if (IS_MCDUFFIN(Store[x][y]))
2188 game_mm.game_over = TRUE;
2189 game_mm.game_over_cause = GAME_OVER_BOMB;
2190 Store[x][y] = EL_EMPTY;
2193 Feld[x][y] = Store[x][y];
2194 Store[x][y] = Store2[x][y] = 0;
2195 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2197 InitField(x, y, FALSE);
2200 else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2202 int graphic = IMG_MM_DEFAULT_EXPLODING;
2203 int graphic_phase = (phase / delay - 1);
2207 if (Store2[x][y] == EX_KETTLE)
2209 if (graphic_phase < 3)
2211 graphic = IMG_MM_KETTLE_EXPLODING;
2213 else if (graphic_phase < 5)
2219 graphic = IMG_EMPTY;
2223 else if (Store2[x][y] == EX_SHORT)
2225 if (graphic_phase < 4)
2231 graphic = IMG_EMPTY;
2236 getGraphicSource(graphic, graphic_phase, &bitmap, &src_x, &src_y);
2238 BlitBitmap(bitmap, drawto_field, src_x, src_y, TILEX, TILEY,
2239 FX + x * TILEX, FY + y * TILEY);
2241 MarkTileDirty(x, y);
2245 static void Bang_MM(int x, int y)
2247 int element = Feld[x][y];
2248 int mode = EX_NORMAL;
2251 DrawLaser(0, DL_LASER_ENABLED);
2270 if (IS_PACMAN(element))
2271 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2272 else if (element == EL_BOMB || IS_MCDUFFIN(element))
2273 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2274 else if (element == EL_KEY)
2275 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2277 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2279 Explode_MM(x, y, EX_PHASE_START, mode);
2282 void TurnRound(int x, int y)
2294 { 0, 0 }, { 0, 0 }, { 0, 0 },
2299 int left, right, back;
2303 { MV_DOWN, MV_UP, MV_RIGHT },
2304 { MV_UP, MV_DOWN, MV_LEFT },
2306 { MV_LEFT, MV_RIGHT, MV_DOWN },
2307 { 0,0,0 }, { 0,0,0 }, { 0,0,0 },
2308 { MV_RIGHT, MV_LEFT, MV_UP }
2311 int element = Feld[x][y];
2312 int old_move_dir = MovDir[x][y];
2313 int right_dir = turn[old_move_dir].right;
2314 int back_dir = turn[old_move_dir].back;
2315 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
2316 int right_x = x + right_dx, right_y = y + right_dy;
2318 if (element == EL_PACMAN)
2320 boolean can_turn_right = FALSE;
2322 if (IN_LEV_FIELD(right_x, right_y) &&
2323 IS_EATABLE4PACMAN(Feld[right_x][right_y]))
2324 can_turn_right = TRUE;
2327 MovDir[x][y] = right_dir;
2329 MovDir[x][y] = back_dir;
2335 static void StartMoving_MM(int x, int y)
2337 int element = Feld[x][y];
2342 if (CAN_MOVE(element))
2346 if (MovDelay[x][y]) /* wait some time before next movement */
2354 /* now make next step */
2356 Moving2Blocked_MM(x, y, &newx, &newy); /* get next screen position */
2358 if (element == EL_PACMAN &&
2359 IN_LEV_FIELD(newx, newy) && IS_EATABLE4PACMAN(Feld[newx][newy]) &&
2360 !ObjHit(newx, newy, HIT_POS_CENTER))
2362 Store[newx][newy] = Feld[newx][newy];
2363 Feld[newx][newy] = EL_EMPTY;
2365 DrawField_MM(newx, newy);
2367 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy) ||
2368 ObjHit(newx, newy, HIT_POS_CENTER))
2370 /* object was running against a wall */
2377 InitMovingField_MM(x, y, MovDir[x][y]);
2381 ContinueMoving_MM(x, y);
2384 static void ContinueMoving_MM(int x, int y)
2386 int element = Feld[x][y];
2387 int direction = MovDir[x][y];
2388 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2389 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2390 int horiz_move = (dx!=0);
2391 int newx = x + dx, newy = y + dy;
2392 int step = (horiz_move ? dx : dy) * TILEX / 8;
2394 MovPos[x][y] += step;
2396 if (ABS(MovPos[x][y]) >= TILEX) /* object reached its destination */
2398 Feld[x][y] = EL_EMPTY;
2399 Feld[newx][newy] = element;
2401 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2402 MovDelay[newx][newy] = 0;
2404 if (!CAN_MOVE(element))
2405 MovDir[newx][newy] = 0;
2408 DrawField_MM(newx, newy);
2410 Stop[newx][newy] = TRUE;
2412 if (element == EL_PACMAN)
2414 if (Store[newx][newy] == EL_BOMB)
2415 Bang_MM(newx, newy);
2417 if (IS_WALL_AMOEBA(Store[newx][newy]) &&
2418 (LX + 2 * XS) / TILEX == newx &&
2419 (LY + 2 * YS) / TILEY == newy)
2426 else /* still moving on */
2431 laser.redraw = TRUE;
2434 void ClickElement(int x, int y, int button)
2436 static unsigned int click_delay = 0;
2437 static int click_delay_value = CLICK_DELAY_SHORT;
2438 static boolean new_button = TRUE;
2441 /* do not rotate objects hit by the laser after the game was solved */
2442 if (game_mm.level_solved && Hit[x][y])
2445 if (button == MB_RELEASED)
2448 click_delay_value = CLICK_DELAY_SHORT;
2450 /* release eventually hold auto-rotating mirror */
2451 RotateMirror(x, y, MB_RELEASED);
2456 if (!DelayReached(&click_delay, click_delay_value) && !new_button)
2459 if (button == MB_MIDDLEBUTTON) /* middle button has no function */
2462 if (!IN_LEV_FIELD(x, y))
2465 if (Feld[x][y] == EL_EMPTY)
2468 element = Feld[x][y];
2470 if (IS_MIRROR(element) ||
2471 IS_BEAMER(element) ||
2472 IS_POLAR(element) ||
2473 IS_POLAR_CROSS(element) ||
2474 IS_DF_MIRROR(element) ||
2475 IS_DF_MIRROR_AUTO(element))
2477 RotateMirror(x, y, button);
2479 else if (IS_MCDUFFIN(element))
2481 if (!laser.fuse_off)
2483 DrawLaser(0, DL_LASER_DISABLED);
2490 element = get_rotated_element(element, BUTTON_ROTATION(button));
2491 laser.start_angle = get_element_angle(element);
2495 Feld[x][y] = element;
2502 if (!laser.fuse_off)
2505 else if (element == EL_FUSE_ON && laser.fuse_off)
2507 if (x != laser.fuse_x || y != laser.fuse_y)
2510 laser.fuse_off = FALSE;
2511 laser.fuse_x = laser.fuse_y = -1;
2513 DrawGraphic_MM(x, y, IMG_MM_FUSE_ACTIVE);
2516 else if (element == EL_FUSE_ON && !laser.fuse_off && new_button)
2518 laser.fuse_off = TRUE;
2521 laser.overloaded = FALSE;
2523 DrawLaser(0, DL_LASER_DISABLED);
2524 DrawGraphic_MM(x, y, IMG_MM_FUSE);
2526 else if (element == EL_LIGHTBALL)
2530 DrawLaser(0, DL_LASER_ENABLED);
2533 click_delay_value = (new_button ? CLICK_DELAY_LONG : CLICK_DELAY_SHORT);
2537 void RotateMirror(int x, int y, int button)
2539 static int hold_x = -1, hold_y = -1;
2541 if (button == MB_RELEASED)
2543 /* release eventually hold auto-rotating mirror */
2550 if (IS_MIRROR(Feld[x][y]) ||
2551 IS_POLAR_CROSS(Feld[x][y]) ||
2552 IS_POLAR(Feld[x][y]) ||
2553 IS_BEAMER(Feld[x][y]) ||
2554 IS_DF_MIRROR(Feld[x][y]) ||
2555 IS_GRID_STEEL_AUTO(Feld[x][y]) ||
2556 IS_GRID_WOOD_AUTO(Feld[x][y]))
2558 Feld[x][y] = get_rotated_element(Feld[x][y], BUTTON_ROTATION(button));
2560 else if (IS_DF_MIRROR_AUTO(Feld[x][y]))
2562 if (button == MB_LEFTBUTTON)
2564 /* left mouse button only for manual adjustment, no auto-rotating;
2565 freeze mirror for until mouse button released */
2569 else if (button == MB_RIGHTBUTTON && (hold_x != x || hold_y != y))
2571 Feld[x][y] = get_rotated_element(Feld[x][y], ROTATE_RIGHT);
2575 if (IS_GRID_STEEL_AUTO(Feld[x][y]) || IS_GRID_WOOD_AUTO(Feld[x][y]))
2577 int edge = Hit[x][y];
2583 DrawLaser(edge - 1, DL_LASER_DISABLED);
2587 else if (ObjHit(x, y, HIT_POS_CENTER))
2589 int edge = Hit[x][y];
2593 Error(ERR_WARN, "RotateMirror: inconsistent field Hit[][]!\n");
2597 DrawLaser(edge - 1, DL_LASER_DISABLED);
2604 if (ObjHit(x, y, HIT_POS_EDGE | HIT_POS_BETWEEN))
2609 if ((IS_BEAMER(Feld[x][y]) ||
2610 IS_POLAR(Feld[x][y]) ||
2611 IS_POLAR_CROSS(Feld[x][y])) && x == ELX && y == ELY)
2615 if (IS_BEAMER(Feld[x][y]))
2618 printf("TEST (%d, %d) [%d] [%d]\n",
2620 laser.beamer_edge, laser.beamer[1].num);
2630 DrawLaser(0, DL_LASER_ENABLED);
2634 void AutoRotateMirrors()
2636 static unsigned int rotate_delay = 0;
2639 if (!DelayReached(&rotate_delay, AUTO_ROTATE_DELAY))
2642 for (x = 0; x < lev_fieldx; x++)
2644 for (y = 0; y < lev_fieldy; y++)
2646 int element = Feld[x][y];
2648 /* do not rotate objects hit by the laser after the game was solved */
2649 if (game_mm.level_solved && Hit[x][y])
2652 if (IS_DF_MIRROR_AUTO(element) ||
2653 IS_GRID_WOOD_AUTO(element) ||
2654 IS_GRID_STEEL_AUTO(element) ||
2655 element == EL_REFRACTOR)
2656 RotateMirror(x, y, MB_RIGHTBUTTON);
2661 boolean ObjHit(int obx, int oby, int bits)
2668 if (bits & HIT_POS_CENTER)
2670 if (ReadPixel(drawto, SX + obx + 15, SY + oby + 15) == pen_ray)
2674 if (bits & HIT_POS_EDGE)
2676 for (i = 0; i < 4; i++)
2677 if (ReadPixel(drawto,
2678 SX + obx + 31 * (i % 2),
2679 SY + oby + 31 * (i / 2)) == pen_ray)
2683 if (bits & HIT_POS_BETWEEN)
2685 for (i = 0; i < 4; i++)
2686 if (ReadPixel(drawto,
2687 SX + 4 + obx + 22 * (i % 2),
2688 SY + 4 + oby + 22 * (i / 2)) == pen_ray)
2695 void DeletePacMan(int px, int py)
2701 if (game_mm.num_pacman <= 1)
2703 game_mm.num_pacman = 0;
2707 for (i = 0; i < game_mm.num_pacman; i++)
2708 if (game_mm.pacman[i].x == px && game_mm.pacman[i].y == py)
2711 game_mm.num_pacman--;
2713 for (j = i; j < game_mm.num_pacman; j++)
2715 game_mm.pacman[j].x = game_mm.pacman[j + 1].x;
2716 game_mm.pacman[j].y = game_mm.pacman[j + 1].y;
2717 game_mm.pacman[j].dir = game_mm.pacman[j + 1].dir;
2721 void ColorCycling(void)
2723 static int CC, Cc = 0;
2725 static int color, old = 0xF00, new = 0x010, mult = 1;
2726 static unsigned short red, green, blue;
2728 if (color_status == STATIC_COLORS)
2733 if (CC < Cc || CC > Cc + 50)
2737 color = old + new * mult;
2743 if (ABS(mult) == 16)
2753 red = 0x0e00 * ((color & 0xF00) >> 8);
2754 green = 0x0e00 * ((color & 0x0F0) >> 4);
2755 blue = 0x0e00 * ((color & 0x00F));
2756 SetRGB(pen_magicolor[0], red, green, blue);
2758 red = 0x1111 * ((color & 0xF00) >> 8);
2759 green = 0x1111 * ((color & 0x0F0) >> 4);
2760 blue = 0x1111 * ((color & 0x00F));
2761 SetRGB(pen_magicolor[1], red, green, blue);
2765 static void GameActions_MM_Ext(struct MouseActionInfo action, boolean warp_mode)
2767 static unsigned int action_delay = 0;
2768 static unsigned int pacman_delay = 0;
2769 static unsigned int energy_delay = 0;
2770 static unsigned int overload_delay = 0;
2776 WaitUntilDelayReached(&action_delay, GameFrameDelay);
2778 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2781 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2783 element = Feld[x][y];
2785 if (!IS_MOVING(x, y) && CAN_MOVE(element))
2786 StartMoving_MM(x, y);
2787 else if (IS_MOVING(x, y))
2788 ContinueMoving_MM(x, y);
2789 else if (IS_EXPLODING(element))
2790 Explode_MM(x, y, Frame[x][y], EX_NORMAL);
2791 else if (element == EL_EXIT_OPENING)
2793 else if (element == EL_GRAY_BALL_OPENING)
2794 OpenSurpriseBall(x, y);
2795 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_ICE)
2797 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_AMOEBA)
2801 AutoRotateMirrors();
2804 /* !!! CHANGE THIS: REDRAW ONLY WHEN NEEDED !!! */
2806 /* redraw after Explode_MM() ... */
2808 DrawLaser(0, DL_LASER_ENABLED);
2809 laser.redraw = FALSE;
2814 if (game_mm.num_pacman && DelayReached(&pacman_delay, 250))
2818 if (laser.num_damages > MAX_LASER_LEN && !laser.fuse_off)
2820 DrawLaser(0, DL_LASER_DISABLED);
2825 if (DelayReached(&energy_delay, 4000))
2827 game_mm.energy_left--;
2828 if (game_mm.energy_left >= 0)
2831 BlitBitmap(pix[PIX_DOOR], drawto,
2832 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
2833 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
2834 DX_ENERGY, DY_ENERGY);
2836 redraw_mask |= REDRAW_DOOR_1;
2838 else if (setup.time_limit)
2842 for (i = 15; i >= 0; i--)
2845 SetRGB(pen_ray, 0x0000, 0x0000, i * color_scale);
2847 pen_ray = GetPixelFromRGB(window,
2848 native_mm_level.laser_red * 0x11 * i,
2849 native_mm_level.laser_green * 0x11 * i,
2850 native_mm_level.laser_blue * 0x11 * i);
2852 DrawLaser(0, DL_LASER_ENABLED);
2857 StopSound_MM(SND_MM_GAME_HEALTH_CHARGING);
2860 DrawLaser(0, DL_LASER_DISABLED);
2861 game_mm.game_over = TRUE;
2862 game_mm.game_over_cause = GAME_OVER_NO_ENERGY;
2865 if (Request("Out of magic energy ! Play it again ?",
2866 REQ_ASK | REQ_STAY_CLOSED))
2872 game_status = MAINMENU;
2881 element = laser.dest_element;
2884 if (element != Feld[ELX][ELY])
2886 printf("element == %d, Feld[ELX][ELY] == %d\n",
2887 element, Feld[ELX][ELY]);
2891 if (!laser.overloaded && laser.overload_value == 0 &&
2892 element != EL_BOMB &&
2893 element != EL_MINE &&
2894 element != EL_BALL_GRAY &&
2895 element != EL_BLOCK_STONE &&
2896 element != EL_BLOCK_WOOD &&
2897 element != EL_FUSE_ON &&
2898 element != EL_FUEL_FULL &&
2899 !IS_WALL_ICE(element) &&
2900 !IS_WALL_AMOEBA(element))
2903 if (((laser.overloaded && laser.overload_value < MAX_LASER_OVERLOAD) ||
2904 (!laser.overloaded && laser.overload_value > 0)) &&
2905 DelayReached(&overload_delay, 60 + !laser.overloaded * 120))
2907 if (laser.overloaded)
2908 laser.overload_value++;
2910 laser.overload_value--;
2912 if (game_mm.cheat_no_overload)
2914 laser.overloaded = FALSE;
2915 laser.overload_value = 0;
2918 game_mm.laser_overload_value = laser.overload_value;
2920 if (laser.overload_value < MAX_LASER_OVERLOAD - 8)
2922 int color_up = 0xFF * laser.overload_value / MAX_LASER_OVERLOAD;
2923 int color_down = 0xFF - color_up;
2926 SetRGB(pen_ray, (laser.overload_value / 6) * color_scale, 0x0000,
2927 (15 - (laser.overload_value / 6)) * color_scale);
2930 GetPixelFromRGB(window,
2931 (native_mm_level.laser_red ? 0xFF : color_up),
2932 (native_mm_level.laser_green ? color_down : 0x00),
2933 (native_mm_level.laser_blue ? color_down : 0x00));
2935 DrawLaser(0, DL_LASER_ENABLED);
2939 if (!laser.overloaded)
2940 StopSound_MM(SND_MM_GAME_HEALTH_CHARGING);
2941 else if (setup.sound_loops)
2942 PlaySoundLoop_MM(SND_MM_GAME_HEALTH_CHARGING);
2944 PlaySound_MM(SND_MM_GAME_HEALTH_CHARGING);
2946 if (laser.overloaded)
2949 BlitBitmap(pix[PIX_DOOR], drawto,
2950 DOOR_GFX_PAGEX4 + XX_OVERLOAD,
2951 DOOR_GFX_PAGEY1 + YY_OVERLOAD + OVERLOAD_YSIZE
2952 - laser.overload_value,
2953 OVERLOAD_XSIZE, laser.overload_value,
2954 DX_OVERLOAD, DY_OVERLOAD + OVERLOAD_YSIZE
2955 - laser.overload_value);
2957 redraw_mask |= REDRAW_DOOR_1;
2962 BlitBitmap(pix[PIX_DOOR], drawto,
2963 DOOR_GFX_PAGEX5 + XX_OVERLOAD, DOOR_GFX_PAGEY1 + YY_OVERLOAD,
2964 OVERLOAD_XSIZE, OVERLOAD_YSIZE - laser.overload_value,
2965 DX_OVERLOAD, DY_OVERLOAD);
2967 redraw_mask |= REDRAW_DOOR_1;
2970 if (laser.overload_value == MAX_LASER_OVERLOAD)
2974 for (i = 15; i >= 0; i--)
2977 SetRGB(pen_ray, i * color_scale, 0x0000, 0x0000);
2980 pen_ray = GetPixelFromRGB(window, 0x11 * i, 0x00, 0x00);
2982 DrawLaser(0, DL_LASER_ENABLED);
2987 DrawLaser(0, DL_LASER_DISABLED);
2989 game_mm.game_over = TRUE;
2990 game_mm.game_over_cause = GAME_OVER_OVERLOADED;
2993 if (Request("Magic spell hit Mc Duffin ! Play it again ?",
2994 REQ_ASK | REQ_STAY_CLOSED))
3000 game_status = MAINMENU;
3014 if (element == EL_BOMB && CT > 1500)
3016 if (game_mm.cheat_no_explosion)
3020 laser.num_damages--;
3021 DrawLaser(0, DL_LASER_DISABLED);
3022 laser.num_edges = 0;
3027 laser.dest_element = EL_EXPLODING_OPAQUE;
3031 laser.num_damages--;
3032 DrawLaser(0, DL_LASER_DISABLED);
3034 laser.num_edges = 0;
3035 Bang_MM(laser.start_edge.x, laser.start_edge.y);
3037 if (Request("Bomb killed Mc Duffin ! Play it again ?",
3038 REQ_ASK | REQ_STAY_CLOSED))
3044 game_status = MAINMENU;
3052 if (element == EL_FUSE_ON && CT > 500)
3054 laser.fuse_off = TRUE;
3058 DrawLaser(0, DL_LASER_DISABLED);
3059 DrawGraphic_MM(ELX, ELY, IMG_MM_FUSE);
3062 if (element == EL_BALL_GRAY && CT > 1500)
3064 static int new_elements[] =
3067 EL_MIRROR_FIXED_START,
3069 EL_POLAR_CROSS_START,
3075 int num_new_elements = sizeof(new_elements) / sizeof(int);
3076 int new_element = new_elements[RND(num_new_elements)];
3078 Store[ELX][ELY] = new_element + RND(get_num_elements(new_element));
3079 Feld[ELX][ELY] = EL_GRAY_BALL_OPENING;
3081 /* !!! CHECK AGAIN: Laser on Polarizer !!! */
3092 element = EL_MIRROR_START + RND(16);
3098 element = (rnd == 0 ? EL_KETTLE : rnd == 1 ? EL_BOMB : EL_PRISM);
3105 element = (rnd == 0 ? EL_FUSE_ON :
3106 rnd >= 1 && rnd <= 4 ? EL_PACMAN_RIGHT + rnd - 1 :
3107 rnd >= 5 && rnd <= 20 ? EL_POLAR_START + rnd - 5 :
3108 rnd >= 21 && rnd <= 24 ? EL_POLAR_CROSS_START + rnd - 21 :
3109 EL_MIRROR_FIXED_START + rnd - 25);
3114 graphic = el2gfx(element);
3116 for (i = 0; i < 50; i++)
3122 BlitBitmap(pix[PIX_BACK], drawto,
3123 SX + (graphic % GFX_PER_LINE) * TILEX + x,
3124 SY + (graphic / GFX_PER_LINE) * TILEY + y, 6, 6,
3125 SX + ELX * TILEX + x,
3126 SY + ELY * TILEY + y);
3128 MarkTileDirty(ELX, ELY);
3131 DrawLaser(0, DL_LASER_ENABLED);
3136 Feld[ELX][ELY] = element;
3137 DrawField_MM(ELX, ELY);
3140 printf("NEW ELEMENT: (%d, %d)\n", ELX, ELY);
3143 /* above stuff: GRAY BALL -> PRISM !!! */
3145 LX = ELX * TILEX + 14;
3146 LY = ELY * TILEY + 14;
3147 if (laser.current_angle == (laser.current_angle >> 1) << 1)
3154 laser.num_edges -= 2;
3155 laser.num_damages--;
3159 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
3160 if (laser.damage[i].is_mirror)
3164 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3166 DrawLaser(0, DL_LASER_DISABLED);
3168 DrawLaser(0, DL_LASER_DISABLED);
3174 printf("TEST ELEMENT: %d\n", Feld[0][0]);
3181 if (IS_WALL_ICE(element) && CT > 1000)
3183 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_SHRINKING);
3186 Feld[ELX][ELY] = Feld[ELX][ELY] - EL_WALL_ICE + EL_WALL_CHANGING;
3187 Store[ELX][ELY] = EL_WALL_ICE;
3188 Store2[ELX][ELY] = laser.wall_mask;
3190 laser.dest_element = Feld[ELX][ELY];
3195 for (i = 0; i < 5; i++)
3201 Feld[ELX][ELY] &= (laser.wall_mask ^ 0xFF);
3205 DrawWallsAnimation_MM(ELX, ELY, Feld[ELX][ELY], phase, laser.wall_mask);
3210 if (Feld[ELX][ELY] == EL_WALL_ICE)
3211 Feld[ELX][ELY] = EL_EMPTY;
3215 LX = laser.edge[laser.num_edges].x - (SX + 2);
3216 LY = laser.edge[laser.num_edges].y - (SY + 2);
3219 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
3220 if (laser.damage[i].is_mirror)
3224 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3226 DrawLaser(0, DL_LASER_DISABLED);
3233 if (IS_WALL_AMOEBA(element) && CT > 1200)
3235 int k1, k2, k3, dx, dy, de, dm;
3236 int element2 = Feld[ELX][ELY];
3238 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element))
3241 for (i = laser.num_damages - 1; i >= 0; i--)
3242 if (laser.damage[i].is_mirror)
3245 r = laser.num_edges;
3246 d = laser.num_damages;
3253 DrawLaser(laser.damage[k1].edge - 1, DL_LASER_DISABLED);
3256 DrawLaser(0, DL_LASER_ENABLED);
3259 x = laser.damage[k1].x;
3260 y = laser.damage[k1].y;
3265 for (i = 0; i < 4; i++)
3267 if (laser.wall_mask & (1 << i))
3269 if (ReadPixel(drawto,
3270 SX + ELX * TILEX + 14 + (i % 2) * 2,
3271 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3273 if (ReadPixel(drawto,
3274 SX + ELX * TILEX + 31 * (i % 2),
3275 SY + ELY * TILEY + 14 + (i / 2) * 2) == pen_ray)
3282 for (i = 0; i < 4; i++)
3284 if (laser.wall_mask & (1 << i))
3286 if (ReadPixel(drawto,
3287 SX + ELX * TILEX + 31 * (i % 2),
3288 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3295 if (laser.num_beamers > 0 ||
3296 k1 < 1 || k2 < 4 || k3 < 4 ||
3297 ReadPixel(drawto, SX + ELX * TILEX + 14, SY + ELY * TILEY + 14)
3300 laser.num_edges = r;
3301 laser.num_damages = d;
3303 DrawLaser(0, DL_LASER_DISABLED);
3306 Feld[ELX][ELY] = element | laser.wall_mask;
3310 de = Feld[ELX][ELY];
3311 dm = laser.wall_mask;
3315 int x = ELX, y = ELY;
3316 int wall_mask = laser.wall_mask;
3319 DrawLaser(0, DL_LASER_ENABLED);
3321 PlayLevelSound_MM(dx, dy, element, MM_ACTION_GROWING);
3323 Feld[x][y] = Feld[x][y] - EL_WALL_AMOEBA + EL_WALL_CHANGING;
3324 Store[x][y] = EL_WALL_AMOEBA;
3325 Store2[x][y] = wall_mask;
3331 DrawWallsAnimation_MM(dx, dy, de, 4, dm);
3333 DrawLaser(0, DL_LASER_ENABLED);
3335 PlayLevelSound_MM(dx, dy, element, MM_ACTION_GROWING);
3337 for (i = 4; i >= 0; i--)
3339 DrawWallsAnimation_MM(dx, dy, de, i, dm);
3345 DrawLaser(0, DL_LASER_ENABLED);
3350 if ((element == EL_BLOCK_WOOD || element == EL_BLOCK_STONE) &&
3351 laser.stops_inside_element && CT > 1500)
3356 if (ABS(XS) > ABS(YS))
3363 for (i = 0; i < 4; i++)
3370 x = ELX + Step[k * 4].x;
3371 y = ELY + Step[k * 4].y;
3373 if (!IN_LEV_FIELD(x, y) || Feld[x][y] != EL_EMPTY)
3376 if (ObjHit(x, y, HIT_POS_CENTER | HIT_POS_EDGE | HIT_POS_BETWEEN))
3384 laser.overloaded = (element == EL_BLOCK_STONE);
3389 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_PUSHING);
3392 Feld[x][y] = element;
3394 DrawGraphic_MM(ELX, ELY, IMG_EMPTY);
3397 if (element == EL_BLOCK_STONE && Box[ELX][ELY])
3399 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
3400 DrawLaser(laser.num_edges - 1, DL_LASER_ENABLED);
3408 if (element == EL_FUEL_FULL && CT > 200)
3410 for (i = game_mm.energy_left; i <= MAX_LASER_ENERGY; i+=2)
3413 BlitBitmap(pix[PIX_DOOR], drawto,
3414 DOOR_GFX_PAGEX4 + XX_ENERGY,
3415 DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
3416 ENERGY_XSIZE, i, DX_ENERGY,
3417 DY_ENERGY + ENERGY_YSIZE - i);
3420 redraw_mask |= REDRAW_DOOR_1;
3426 game_mm.energy_left = MAX_LASER_ENERGY;
3427 Feld[ELX][ELY] = EL_FUEL_EMPTY;
3428 DrawField_MM(ELX, ELY);
3430 DrawLaser(0, DL_LASER_ENABLED);
3438 void GameActions_MM(struct MouseActionInfo action, boolean warp_mode)
3440 ClickElement(action.lx, action.ly, action.button);
3442 GameActions_MM_Ext(action, warp_mode);
3448 int mx, my, ox, oy, nx, ny;
3452 if (++p >= game_mm.num_pacman)
3455 game_mm.pacman[p].dir--;
3457 for (l = 1; l < 5; l++)
3459 game_mm.pacman[p].dir++;
3461 if (game_mm.pacman[p].dir > 4)
3462 game_mm.pacman[p].dir = 1;
3464 if (game_mm.pacman[p].dir % 2)
3467 my = game_mm.pacman[p].dir - 2;
3472 mx = 3 - game_mm.pacman[p].dir;
3475 ox = game_mm.pacman[p].x;
3476 oy = game_mm.pacman[p].y;
3479 element = Feld[nx][ny];
3481 if (nx < 0 || nx > 15 || ny < 0 || ny > 11)
3484 if (!IS_EATABLE4PACMAN(element))
3487 if (ObjHit(nx, ny, HIT_POS_CENTER))
3490 Feld[ox][oy] = EL_EMPTY;
3492 EL_PACMAN_RIGHT - 1 +
3493 (game_mm.pacman[p].dir - 1 +
3494 (game_mm.pacman[p].dir % 2) * 2);
3496 game_mm.pacman[p].x = nx;
3497 game_mm.pacman[p].y = ny;
3499 DrawGraphic_MM(ox, oy, IMG_EMPTY);
3501 if (element != EL_EMPTY)
3503 int graphic = el2gfx(Feld[nx][ny]);
3508 getGraphicSource(graphic, 0, &bitmap, &src_x, &src_y);
3511 ox = SX + ox * TILEX;
3512 oy = SY + oy * TILEY;
3514 for (i = 1; i < 33; i += 2)
3515 BlitBitmap(bitmap, window,
3516 src_x, src_y, TILEX, TILEY,
3517 ox + i * mx, oy + i * my);
3518 Ct = Ct + Counter() - CT;
3521 DrawField_MM(nx, ny);
3524 if (!laser.fuse_off)
3526 DrawLaser(0, DL_LASER_ENABLED);
3528 if (ObjHit(nx, ny, HIT_POS_BETWEEN))
3530 AddDamagedField(nx, ny);
3532 laser.damage[laser.num_damages - 1].edge = 0;
3536 if (element == EL_BOMB)
3537 DeletePacMan(nx, ny);
3539 if (IS_WALL_AMOEBA(element) &&
3540 (LX + 2 * XS) / TILEX == nx &&
3541 (LY + 2 * YS) / TILEY == ny)
3554 boolean raise_level = FALSE;
3557 if (local_player->MovPos)
3560 local_player->LevelSolved = FALSE;
3563 if (game_mm.energy_left)
3565 if (setup.sound_loops)
3566 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3567 SND_CTRL_PLAY_LOOP);
3569 while (game_mm.energy_left > 0)
3571 if (!setup.sound_loops)
3572 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3575 if (game_mm.energy_left > 0 && !(game_mm.energy_left % 10))
3576 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3581 game_mm.energy_left--;
3582 if (game_mm.energy_left >= 0)
3585 BlitBitmap(pix[PIX_DOOR], drawto,
3586 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
3587 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
3588 DX_ENERGY, DY_ENERGY);
3590 redraw_mask |= REDRAW_DOOR_1;
3597 if (setup.sound_loops)
3598 StopSound(SND_SIRR);
3600 else if (native_mm_level.time == 0) /* level without time limit */
3602 if (setup.sound_loops)
3603 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3604 SND_CTRL_PLAY_LOOP);
3606 while (TimePlayed < 999)
3608 if (!setup.sound_loops)
3609 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3610 if (TimePlayed < 999 && !(TimePlayed % 10))
3611 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3612 if (TimePlayed < 900 && !(TimePlayed % 10))
3618 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
3625 if (setup.sound_loops)
3626 StopSound(SND_SIRR);
3633 CloseDoor(DOOR_CLOSE_1);
3635 Request("Level solved !", REQ_CONFIRM);
3637 if (level_nr == leveldir_current->handicap_level)
3639 leveldir_current->handicap_level++;
3640 SaveLevelSetup_SeriesInfo();
3643 if (level_editor_test_game)
3644 game_mm.score = -1; /* no highscore when playing from editor */
3645 else if (level_nr < leveldir_current->last_level)
3646 raise_level = TRUE; /* advance to next level */
3648 if ((hi_pos = NewHiScore_MM()) >= 0)
3650 game_status = HALLOFFAME;
3652 // DrawHallOfFame(hi_pos);
3659 game_status = MAINMENU;
3675 // LoadScore(level_nr);
3677 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
3678 game_mm.score < highscore[MAX_SCORE_ENTRIES - 1].Score)
3681 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3683 if (game_mm.score > highscore[k].Score)
3685 /* player has made it to the hall of fame */
3687 if (k < MAX_SCORE_ENTRIES - 1)
3689 int m = MAX_SCORE_ENTRIES - 1;
3692 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3693 if (!strcmp(setup.player_name, highscore[l].Name))
3695 if (m == k) /* player's new highscore overwrites his old one */
3699 for (l = m; l>k; l--)
3701 strcpy(highscore[l].Name, highscore[l - 1].Name);
3702 highscore[l].Score = highscore[l - 1].Score;
3709 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3710 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3711 highscore[k].Score = game_mm.score;
3718 else if (!strncmp(setup.player_name, highscore[k].Name,
3719 MAX_PLAYER_NAME_LEN))
3720 break; /* player already there with a higher score */
3725 // if (position >= 0)
3726 // SaveScore(level_nr);
3731 static void InitMovingField_MM(int x, int y, int direction)
3733 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3734 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3736 MovDir[x][y] = direction;
3737 MovDir[newx][newy] = direction;
3739 if (Feld[newx][newy] == EL_EMPTY)
3740 Feld[newx][newy] = EL_BLOCKED;
3743 static void Moving2Blocked_MM(int x, int y, int *goes_to_x, int *goes_to_y)
3745 int direction = MovDir[x][y];
3746 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3747 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3753 static void Blocked2Moving_MM(int x, int y,
3754 int *comes_from_x, int *comes_from_y)
3756 int oldx = x, oldy = y;
3757 int direction = MovDir[x][y];
3759 if (direction == MV_LEFT)
3761 else if (direction == MV_RIGHT)
3763 else if (direction == MV_UP)
3765 else if (direction == MV_DOWN)
3768 *comes_from_x = oldx;
3769 *comes_from_y = oldy;
3772 static int MovingOrBlocked2Element_MM(int x, int y)
3774 int element = Feld[x][y];
3776 if (element == EL_BLOCKED)
3780 Blocked2Moving_MM(x, y, &oldx, &oldy);
3782 return Feld[oldx][oldy];
3789 static void RemoveField(int x, int y)
3791 Feld[x][y] = EL_EMPTY;
3798 static void RemoveMovingField_MM(int x, int y)
3800 int oldx = x, oldy = y, newx = x, newy = y;
3802 if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
3805 if (IS_MOVING(x, y))
3807 Moving2Blocked_MM(x, y, &newx, &newy);
3808 if (Feld[newx][newy] != EL_BLOCKED)
3811 else if (Feld[x][y] == EL_BLOCKED)
3813 Blocked2Moving_MM(x, y, &oldx, &oldy);
3814 if (!IS_MOVING(oldx, oldy))
3818 Feld[oldx][oldy] = EL_EMPTY;
3819 Feld[newx][newy] = EL_EMPTY;
3820 MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
3821 MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
3823 DrawLevelField_MM(oldx, oldy);
3824 DrawLevelField_MM(newx, newy);
3827 void PlaySoundLevel(int x, int y, int sound_nr)
3829 int sx = SCREENX(x), sy = SCREENY(y);
3831 int silence_distance = 8;
3833 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
3834 (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
3837 if (!IN_LEV_FIELD(x, y) ||
3838 sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
3839 sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
3842 volume = SOUND_MAX_VOLUME;
3845 stereo = (sx - SCR_FIELDX/2) * 12;
3847 stereo = SOUND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
3848 if (stereo > SOUND_MAX_RIGHT)
3849 stereo = SOUND_MAX_RIGHT;
3850 if (stereo < SOUND_MAX_LEFT)
3851 stereo = SOUND_MAX_LEFT;
3854 if (!IN_SCR_FIELD(sx, sy))
3856 int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
3857 int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
3859 volume -= volume * (dx > dy ? dx : dy) / silence_distance;
3862 PlaySoundExt(sound_nr, volume, stereo, SND_CTRL_PLAY_SOUND);
3865 static void RaiseScore_MM(int value)
3867 game_mm.score += value;
3870 DrawText(DX_SCORE, DY_SCORE, int2str(game_mm.score, 4),
3875 void RaiseScoreElement_MM(int element)
3880 RaiseScore_MM(native_mm_level.score[SC_PACMAN]);
3884 RaiseScore_MM(native_mm_level.score[SC_KEY]);