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 editor control values */
296 editor.draw_walls_masked = FALSE;
298 /* set global game control values */
299 game_mm.num_cycle = 0;
300 game_mm.num_pacman = 0;
303 game_mm.energy_left = native_mm_level.time;
304 game_mm.kettles_still_needed =
305 (native_mm_level.auto_count_kettles ? 0 : native_mm_level.kettles_needed);
306 game_mm.lights_still_needed = 0;
307 game_mm.num_keys = 0;
309 game_mm.level_solved = FALSE;
310 game_mm.game_over = FALSE;
311 game_mm.game_over_cause = 0;
313 /* set global laser control values (must be set before "InitLaser()") */
314 laser.start_edge.x = 0;
315 laser.start_edge.y = 0;
316 laser.start_angle = 0;
318 for (i = 0; i < MAX_NUM_BEAMERS; i++)
319 laser.beamer[i][0].num = laser.beamer[i][1].num = 0;
321 laser.overloaded = FALSE;
322 laser.overload_value = 0;
323 laser.fuse_off = FALSE;
324 laser.fuse_x = laser.fuse_y = -1;
326 laser.dest_element = EL_EMPTY;
331 for (x = 0; x < lev_fieldx; x++)
333 for (y = 0; y < lev_fieldy; y++)
335 Feld[x][y] = Ur[x][y];
336 Hit[x][y] = Box[x][y] = 0;
338 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
339 Store[x][y] = Store2[x][y] = 0;
343 InitField(x, y, TRUE);
348 CloseDoor(DOOR_CLOSE_1);
354 void InitGameEngine_MM_AfterFadingIn()
360 /* copy default game door content to main double buffer */
361 BlitBitmap(pix[PIX_DOOR], drawto,
362 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
366 DrawText(DX_LEVEL, DY_LEVEL,
367 int2str(level_nr, 2), FONT_TEXT_2);
368 DrawText(DX_KETTLES, DY_KETTLES,
369 int2str(game_mm.kettles_still_needed, 3), FONT_TEXT_2);
370 DrawText(DX_SCORE, DY_SCORE,
371 int2str(game_mm.score, 4), FONT_TEXT_2);
380 /* copy actual game door content to door double buffer for OpenDoor() */
381 BlitBitmap(drawto, pix[PIX_DB_DOOR],
382 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
386 OpenDoor(DOOR_OPEN_ALL);
389 if (setup.sound_loops)
390 PlaySoundExt(SND_FUEL, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT, SND_CTRL_PLAY_LOOP);
392 #if 0 // !!! TEMPORARILY DISABLED !!!
393 for (i = 0; i <= game_mm.energy_left; i += 2)
395 if (!setup.sound_loops)
396 PlaySoundStereo(SND_FUEL, SOUND_MAX_RIGHT);
399 BlitBitmap(pix[PIX_DOOR], drawto,
400 DOOR_GFX_PAGEX4 + XX_ENERGY,
401 DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
403 DX_ENERGY, DY_ENERGY + ENERGY_YSIZE - i);
406 redraw_mask |= REDRAW_DOOR_1;
412 if (setup.quick_doors)
419 if (setup.sound_loops)
424 if (setup.sound_music && num_bg_loops)
425 PlayMusic(level_nr % num_bg_loops);
431 void AddLaserEdge(int lx, int ly)
433 if (lx < -2 || ly < -2 || lx >= SXSIZE + 2 || ly >= SYSIZE + 2)
435 Error(ERR_WARN, "AddLaserEdge: out of bounds: %d, %d", lx, ly);
440 laser.edge[laser.num_edges].x = SX + 2 + lx;
441 laser.edge[laser.num_edges].y = SY + 2 + ly;
447 void AddDamagedField(int ex, int ey)
449 laser.damage[laser.num_damages].is_mirror = FALSE;
450 laser.damage[laser.num_damages].angle = laser.current_angle;
451 laser.damage[laser.num_damages].edge = laser.num_edges;
452 laser.damage[laser.num_damages].x = ex;
453 laser.damage[laser.num_damages].y = ey;
463 int last_x = laser.edge[laser.num_edges - 1].x - SX - 2;
464 int last_y = laser.edge[laser.num_edges - 1].y - SY - 2;
466 return ((x - last_x) * XS < 0 || (y - last_y) * YS < 0);
472 static int getMaskFromElement(int element)
474 if (IS_GRID(element))
475 return IMG_MM_MASK_GRID_1 + get_element_phase(element);
476 else if (IS_MCDUFFIN(element))
477 return IMG_MM_MASK_MCDUFFIN_RIGHT + get_element_phase(element);
478 else if (IS_RECTANGLE(element) || IS_DF_GRID(element))
479 return IMG_MM_MASK_RECTANGLE;
481 return IMG_MM_MASK_CIRCLE;
489 printf("ScanPixel: start scanning at (%d, %d) [%d, %d] [%d, %d]\n",
490 LX, LY, LX / TILEX, LY / TILEY, LX % TILEX, LY % TILEY);
493 /* follow laser beam until it hits something (at least the screen border) */
494 while (hit_mask == HIT_MASK_NO_HIT)
500 if (SX + LX < REAL_SX || SX + LX >= REAL_SX + FULL_SXSIZE ||
501 SY + LY < REAL_SY || SY + LY >= REAL_SY + FULL_SYSIZE)
503 printf("ScanPixel: touched screen border!\n");
509 for (i = 0; i < 4; i++)
511 int px = LX + (i % 2) * 2;
512 int py = LY + (i / 2) * 2;
515 int lx = (px + TILEX) / TILEX - 1; /* ...+TILEX...-1 to get correct */
516 int ly = (py + TILEY) / TILEY - 1; /* negative values! */
519 if (IN_LEV_FIELD(lx, ly))
521 int element = Feld[lx][ly];
523 if (element == EL_EMPTY || element == EL_EXPLODING_TRANSP)
527 else if (IS_WALL(element) || IS_WALL_CHANGING(element))
529 int pos = dy / MINI_TILEY * 2 + dx / MINI_TILEX;
531 pixel = ((element & (1 << pos)) ? 1 : 0);
535 int graphic_mask = getMaskFromElement(element);
540 getGraphicSource(graphic_mask, 0, &bitmap, &src_x, &src_y);
545 pixel = (ReadPixel(bitmap, mask_x, mask_y) ? 1 : 0);
550 pixel = (SX + px < REAL_SX || SX + px >= REAL_SX + FULL_SXSIZE ||
551 SY + py < REAL_SY || SY + py >= REAL_SY + FULL_SYSIZE);
554 if ((Sign[laser.current_angle] & (1 << i)) && pixel)
555 hit_mask |= (1 << i);
558 if (hit_mask == HIT_MASK_NO_HIT)
560 /* hit nothing -- go on with another step */
572 int end = 0, rf = laser.num_edges;
574 /* do not scan laser again after the game was lost for whatever reason */
575 if (game_mm.game_over)
578 laser.overloaded = FALSE;
579 laser.stops_inside_element = FALSE;
581 DrawLaser(0, DL_LASER_ENABLED);
584 printf("Start scanning with LX == %d, LY == %d, XS == %d, YS == %d\n",
592 if (laser.num_edges > MAX_LASER_LEN || laser.num_damages > MAX_LASER_LEN)
595 laser.overloaded = TRUE;
600 hit_mask = ScanPixel();
603 printf("Hit something at LX == %d, LY == %d, XS == %d, YS == %d\n",
607 /* hit something -- check out what it was */
608 ELX = (LX + XS) / TILEX;
609 ELY = (LY + YS) / TILEY;
612 printf("hit_mask (1) == '%x' (%d, %d) (%d, %d)\n",
613 hit_mask, LX, LY, ELX, ELY);
616 if (!IN_LEV_FIELD(ELX, ELY) || !IN_PIX_FIELD(LX, LY))
619 laser.dest_element = element;
624 if (hit_mask == (HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT))
626 /* we have hit the top-right and bottom-left element --
627 choose the bottom-left one */
628 /* !!! THIS CAN BE DONE MORE INTELLIGENTLY, FOR EXAMPLE, IF ONE
629 ELEMENT WAS STEEL AND THE OTHER ONE WAS ICE => ALWAYS CHOOSE
630 THE ICE AND MELT IT AWAY INSTEAD OF OVERLOADING LASER !!! */
631 ELX = (LX - 2) / TILEX;
632 ELY = (LY + 2) / TILEY;
635 if (hit_mask == (HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT))
637 /* we have hit the top-left and bottom-right element --
638 choose the top-left one */
639 /* !!! SEE ABOVE !!! */
640 ELX = (LX - 2) / TILEX;
641 ELY = (LY - 2) / TILEY;
645 printf("hit_mask (2) == '%x' (%d, %d) (%d, %d)\n",
646 hit_mask, LX, LY, ELX, ELY);
649 element = Feld[ELX][ELY];
650 laser.dest_element = element;
653 printf("Hit element %d at (%d, %d) [%d, %d] [%d, %d] [%d]\n",
656 LX % TILEX, LY % TILEY,
661 if (!IN_LEV_FIELD(ELX, ELY))
662 printf("WARNING! (1) %d, %d (%d)\n", ELX, ELY, element);
665 if (element == EL_EMPTY)
667 if (!HitOnlyAnEdge(element, hit_mask))
670 else if (element == EL_FUSE_ON)
672 if (HitPolarizer(element, hit_mask))
675 else if (IS_GRID(element) || IS_DF_GRID(element))
677 if (HitPolarizer(element, hit_mask))
680 else if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD ||
681 element == EL_GATE_STONE || element == EL_GATE_WOOD)
683 if (HitBlock(element, hit_mask))
690 else if (IS_MCDUFFIN(element))
692 if (HitLaserSource(element, hit_mask))
695 else if ((element >= EL_EXIT_CLOSED && element <= EL_EXIT_OPEN) ||
696 IS_RECEIVER(element))
698 if (HitLaserDestination(element, hit_mask))
701 else if (IS_WALL(element))
703 if (IS_WALL_STEEL(element) || IS_DF_WALL_STEEL(element))
705 if (HitReflectingWalls(element, hit_mask))
710 if (HitAbsorbingWalls(element, hit_mask))
716 if (HitElement(element, hit_mask))
721 DrawLaser(rf - 1, DL_LASER_ENABLED);
722 rf = laser.num_edges;
726 if (laser.dest_element != Feld[ELX][ELY])
728 printf("ALARM: laser.dest_element == %d, Feld[ELX][ELY] == %d\n",
729 laser.dest_element, Feld[ELX][ELY]);
733 if (!end && !laser.stops_inside_element && !StepBehind())
736 printf("ScanLaser: Go one step back\n");
742 AddLaserEdge(LX, LY);
746 DrawLaser(rf - 1, DL_LASER_ENABLED);
751 if (!IN_LEV_FIELD(ELX, ELY))
752 printf("WARNING! (2) %d, %d\n", ELX, ELY);
756 void DrawLaserExt(int start_edge, int num_edges, int mode)
762 printf("DrawLaserExt: start_edge, num_edges, mode == %d, %d, %d\n",
763 start_edge, num_edges, mode);
768 Error(ERR_WARN, "DrawLaserExt: start_edge < 0");
775 Error(ERR_WARN, "DrawLaserExt: num_edges < 0");
781 if (mode == DL_LASER_DISABLED)
783 printf("DrawLaser: Delete laser from edge %d\n", start_edge);
787 /* now draw the laser to the backbuffer and (if enabled) to the screen */
788 DrawLines(drawto, &laser.edge[start_edge], num_edges,
789 (mode == DL_LASER_ENABLED ? pen_ray : pen_bg));
791 redraw_mask |= REDRAW_FIELD;
793 if (mode == DL_LASER_ENABLED)
796 /* after the laser was deleted, the "damaged" graphics must be restored */
797 if (laser.num_damages)
799 int damage_start = 0;
802 /* determine the starting edge, from which graphics need to be restored */
805 for (i = 0; i < laser.num_damages; i++)
807 if (laser.damage[i].edge == start_edge + 1)
816 /* restore graphics from this starting edge to the end of damage list */
817 for (i = damage_start; i < laser.num_damages; i++)
819 int lx = laser.damage[i].x;
820 int ly = laser.damage[i].y;
821 int element = Feld[lx][ly];
823 if (Hit[lx][ly] == laser.damage[i].edge)
824 if (!((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
827 if (Box[lx][ly] == laser.damage[i].edge)
830 if (IS_DRAWABLE(element))
831 DrawField_MM(lx, ly);
834 elx = laser.damage[damage_start].x;
835 ely = laser.damage[damage_start].y;
836 element = Feld[elx][ely];
839 if (IS_BEAMER(element))
843 for (i = 0; i < laser.num_beamers; i++)
844 printf("-> %d\n", laser.beamer_edge[i]);
845 printf("DrawLaserExt: IS_BEAMER: [%d]: Hit[%d][%d] == %d [%d]\n",
846 mode, elx, ely, Hit[elx][ely], start_edge);
847 printf("DrawLaserExt: IS_BEAMER: %d / %d\n",
848 get_element_angle(element), laser.damage[damage_start].angle);
852 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
853 laser.num_beamers > 0 &&
854 start_edge == laser.beamer_edge[laser.num_beamers - 1])
856 /* element is outgoing beamer */
857 laser.num_damages = damage_start + 1;
859 if (IS_BEAMER(element))
860 laser.current_angle = get_element_angle(element);
864 /* element is incoming beamer or other element */
865 laser.num_damages = damage_start;
866 laser.current_angle = laser.damage[laser.num_damages].angle;
871 /* no damages but McDuffin himself (who needs to be redrawn anyway) */
873 elx = laser.start_edge.x;
874 ely = laser.start_edge.y;
875 element = Feld[elx][ely];
878 laser.num_edges = start_edge + 1;
880 laser.current_angle = laser.start_angle;
882 LX = laser.edge[start_edge].x - (SX + 2);
883 LY = laser.edge[start_edge].y - (SY + 2);
884 XS = 2 * Step[laser.current_angle].x;
885 YS = 2 * Step[laser.current_angle].y;
888 printf("DrawLaser: Set (LX, LY) to (%d, %d) [%d]\n",
894 if (IS_BEAMER(element) ||
895 IS_FIBRE_OPTIC(element) ||
896 IS_PACMAN(element) ||
898 IS_POLAR_CROSS(element) ||
899 element == EL_FUSE_ON)
904 printf("element == %d\n", element);
907 if (IS_22_5_ANGLE(laser.current_angle)) /* neither 90° nor 45° angle */
908 step_size = ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) ? 4 : 3);
912 if (IS_POLAR(element) || IS_POLAR_CROSS(element) ||
913 ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
914 (laser.num_beamers == 0 ||
915 start_edge != laser.beamer_edge[laser.num_beamers - 1])))
917 /* element is incoming beamer or other element */
918 step_size = -step_size;
923 if (IS_BEAMER(element))
925 printf("start_edge == %d, laser.beamer_edge == %d\n",
926 start_edge, laser.beamer_edge);
930 LX += step_size * XS;
931 LY += step_size * YS;
933 else if (element != EL_EMPTY)
942 printf("DrawLaser: Finally: (LX, LY) to (%d, %d) [%d]\n",
947 void DrawLaser(int start_edge, int mode)
949 if (laser.num_edges - start_edge < 0)
951 Error(ERR_WARN, "DrawLaser: laser.num_edges - start_edge < 0");
956 /* check if laser is interrupted by beamer element */
957 if (laser.num_beamers > 0 &&
958 start_edge < laser.beamer_edge[laser.num_beamers - 1])
960 if (mode == DL_LASER_ENABLED)
963 int tmp_start_edge = start_edge;
965 /* draw laser segments forward from the start to the last beamer */
966 for (i = 0; i < laser.num_beamers; i++)
968 int tmp_num_edges = laser.beamer_edge[i] - tmp_start_edge;
970 if (tmp_num_edges <= 0)
974 printf("DrawLaser: DL_LASER_ENABLED: i==%d: %d, %d\n",
975 i, laser.beamer_edge[i], tmp_start_edge);
978 DrawLaserExt(tmp_start_edge, tmp_num_edges, DL_LASER_ENABLED);
980 tmp_start_edge = laser.beamer_edge[i];
983 /* draw last segment from last beamer to the end */
984 DrawLaserExt(tmp_start_edge, laser.num_edges - tmp_start_edge,
990 int last_num_edges = laser.num_edges;
991 int num_beamers = laser.num_beamers;
993 /* delete laser segments backward from the end to the first beamer */
994 for (i = num_beamers-1; i >= 0; i--)
996 int tmp_num_edges = last_num_edges - laser.beamer_edge[i];
998 if (laser.beamer_edge[i] - start_edge <= 0)
1001 DrawLaserExt(laser.beamer_edge[i], tmp_num_edges, DL_LASER_DISABLED);
1003 last_num_edges = laser.beamer_edge[i];
1004 laser.num_beamers--;
1008 if (last_num_edges - start_edge <= 0)
1009 printf("DrawLaser: DL_LASER_DISABLED: %d, %d\n",
1010 last_num_edges, start_edge);
1013 /* delete first segment from start to the first beamer */
1014 DrawLaserExt(start_edge, last_num_edges - start_edge, DL_LASER_DISABLED);
1019 DrawLaserExt(start_edge, laser.num_edges - start_edge, mode);
1023 boolean HitElement(int element, int hit_mask)
1025 if (HitOnlyAnEdge(element, hit_mask))
1028 if (IS_MOVING(ELX, ELY) || IS_BLOCKED(ELX, ELY))
1029 element = MovingOrBlocked2Element_MM(ELX, ELY);
1032 printf("HitElement (1): element == %d\n", element);
1036 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1037 printf("HitElement (%d): EXACT MATCH @ (%d, %d)\n", element, ELX, ELY);
1039 printf("HitElement (%d): FUZZY MATCH @ (%d, %d)\n", element, ELX, ELY);
1042 AddDamagedField(ELX, ELY);
1044 /* this is more precise: check if laser would go through the center */
1045 if ((ELX * TILEX + 14 - LX) * YS != (ELY * TILEY + 14 - LY) * XS)
1047 /* skip the whole element before continuing the scan */
1053 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1055 if (LX/TILEX > ELX || LY/TILEY > ELY)
1057 /* skipping scan positions to the right and down skips one scan
1058 position too much, because this is only the top left scan position
1059 of totally four scan positions (plus one to the right, one to the
1060 bottom and one to the bottom right) */
1070 printf("HitElement (2): element == %d\n", element);
1073 if (LX + 5 * XS < 0 ||
1083 printf("HitElement (3): element == %d\n", element);
1086 if (IS_POLAR(element) &&
1087 ((element - EL_POLAR_START) % 2 ||
1088 (element - EL_POLAR_START) / 2 != laser.current_angle % 8))
1090 PlaySoundStereo(SND_KINK, ST(ELX));
1092 laser.num_damages--;
1097 if (IS_POLAR_CROSS(element) &&
1098 (element - EL_POLAR_CROSS_START) != laser.current_angle % 4)
1100 PlaySoundStereo(SND_KINK, ST(ELX));
1102 laser.num_damages--;
1107 if (!IS_BEAMER(element) &&
1108 !IS_FIBRE_OPTIC(element) &&
1109 !IS_GRID_WOOD(element) &&
1110 element != EL_FUEL_EMPTY)
1113 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1114 printf("EXACT MATCH @ (%d, %d)\n", ELX, ELY);
1116 printf("FUZZY MATCH @ (%d, %d)\n", ELX, ELY);
1119 LX = ELX * TILEX + 14;
1120 LY = ELY * TILEY + 14;
1122 AddLaserEdge(LX, LY);
1125 if (IS_MIRROR(element) ||
1126 IS_MIRROR_FIXED(element) ||
1127 IS_POLAR(element) ||
1128 IS_POLAR_CROSS(element) ||
1129 IS_DF_MIRROR(element) ||
1130 IS_DF_MIRROR_AUTO(element) ||
1131 element == EL_PRISM ||
1132 element == EL_REFRACTOR)
1134 int current_angle = laser.current_angle;
1137 laser.num_damages--;
1139 AddDamagedField(ELX, ELY);
1141 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1144 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1146 if (IS_MIRROR(element) ||
1147 IS_MIRROR_FIXED(element) ||
1148 IS_DF_MIRROR(element) ||
1149 IS_DF_MIRROR_AUTO(element))
1150 laser.current_angle = get_mirrored_angle(laser.current_angle,
1151 get_element_angle(element));
1153 if (element == EL_PRISM || element == EL_REFRACTOR)
1154 laser.current_angle = RND(16);
1156 XS = 2 * Step[laser.current_angle].x;
1157 YS = 2 * Step[laser.current_angle].y;
1159 if (!IS_22_5_ANGLE(laser.current_angle)) /* 90° or 45° angle */
1164 LX += step_size * XS;
1165 LY += step_size * YS;
1168 /* draw sparkles on mirror */
1169 if ((IS_MIRROR(element) || IS_MIRROR_FIXED(element)) &&
1170 current_angle != laser.current_angle)
1172 MoveSprite(vp, &Pfeil[2], 4 + 16 * ELX, 5 + 16 * ELY + 1);
1176 if ((!IS_POLAR(element) && !IS_POLAR_CROSS(element)) &&
1177 current_angle != laser.current_angle)
1178 PlaySoundStereo(SND_LASER, ST(ELX));
1181 (get_opposite_angle(laser.current_angle) ==
1182 laser.damage[laser.num_damages - 1].angle ? TRUE : FALSE);
1184 return (laser.overloaded ? TRUE : FALSE);
1187 if (element == EL_FUEL_FULL)
1189 laser.stops_inside_element = TRUE;
1194 if (element == EL_BOMB || element == EL_MINE)
1196 PlaySoundStereo(SND_KINK, ST(ELX));
1198 if (element == EL_MINE)
1199 laser.overloaded = TRUE;
1202 if (element == EL_KETTLE ||
1203 element == EL_CELL ||
1204 element == EL_KEY ||
1205 element == EL_LIGHTBALL ||
1206 element == EL_PACMAN ||
1209 if (!IS_PACMAN(element))
1212 if (element == EL_PACMAN)
1215 if (element == EL_KETTLE || element == EL_CELL)
1217 if (game_mm.kettles_still_needed > 0)
1218 game_mm.kettles_still_needed--;
1222 if (game_mm.kettles_still_needed == 0)
1225 static int xy[4][2] =
1233 PlaySoundStereo(SND_KLING, ST(ELX));
1235 for (y = 0; y < lev_fieldy; y++)
1237 for (x = 0; x < lev_fieldx; x++)
1239 /* initiate opening animation of exit door */
1240 if (Feld[x][y] == EL_EXIT_CLOSED)
1241 Feld[x][y] = EL_EXIT_OPENING;
1243 /* remove field that blocks receiver */
1244 if (IS_RECEIVER(Feld[x][y]))
1246 int phase = Feld[x][y] - EL_RECEIVER_START;
1247 int blocking_x, blocking_y;
1249 blocking_x = x + xy[phase][0];
1250 blocking_y = y + xy[phase][1];
1252 if (IN_LEV_FIELD(blocking_x, blocking_y))
1254 Feld[blocking_x][blocking_y] = EL_EMPTY;
1256 DrawField_MM(blocking_x, blocking_y);
1262 DrawLaser(0, DL_LASER_ENABLED);
1265 else if (element == EL_KEY)
1269 else if (element == EL_LIGHTBALL)
1273 else if (IS_PACMAN(element))
1275 DeletePacMan(ELX, ELY);
1282 if (element == EL_LIGHTBULB_OFF || element == EL_LIGHTBULB_ON)
1284 PlaySoundStereo(SND_KINK, ST(ELX));
1286 DrawLaser(0, DL_LASER_ENABLED);
1288 if (Feld[ELX][ELY] == EL_LIGHTBULB_OFF)
1290 Feld[ELX][ELY] = EL_LIGHTBULB_ON;
1291 game_mm.lights_still_needed--;
1295 Feld[ELX][ELY] = EL_LIGHTBULB_OFF;
1296 game_mm.lights_still_needed++;
1299 DrawField_MM(ELX, ELY);
1300 DrawLaser(0, DL_LASER_ENABLED);
1305 laser.stops_inside_element = TRUE;
1311 printf("HitElement (4): element == %d\n", element);
1314 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1315 laser.num_beamers < MAX_NUM_BEAMERS &&
1316 laser.beamer[BEAMER_NR(element)][1].num)
1318 int beamer_angle = get_element_angle(element);
1319 int beamer_nr = BEAMER_NR(element);
1323 printf("HitElement (BEAMER): element == %d\n", element);
1326 laser.num_damages--;
1328 if (IS_FIBRE_OPTIC(element) ||
1329 laser.current_angle == get_opposite_angle(beamer_angle))
1333 LX = ELX * TILEX + 14;
1334 LY = ELY * TILEY + 14;
1336 AddLaserEdge(LX, LY);
1337 AddDamagedField(ELX, ELY);
1339 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1342 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1344 pos = (ELX == laser.beamer[beamer_nr][0].x &&
1345 ELY == laser.beamer[beamer_nr][0].y ? 1 : 0);
1346 ELX = laser.beamer[beamer_nr][pos].x;
1347 ELY = laser.beamer[beamer_nr][pos].y;
1348 LX = ELX * TILEX + 14;
1349 LY = ELY * TILEY + 14;
1351 if (IS_BEAMER(element))
1353 laser.current_angle = get_element_angle(Feld[ELX][ELY]);
1354 XS = 2 * Step[laser.current_angle].x;
1355 YS = 2 * Step[laser.current_angle].y;
1358 laser.beamer_edge[laser.num_beamers] = laser.num_edges;
1360 AddLaserEdge(LX, LY);
1361 AddDamagedField(ELX, ELY);
1363 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1366 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1368 if (laser.current_angle == (laser.current_angle >> 1) << 1)
1373 LX += step_size * XS;
1374 LY += step_size * YS;
1376 laser.num_beamers++;
1385 boolean HitOnlyAnEdge(int element, int hit_mask)
1387 /* check if the laser hit only the edge of an element and, if so, go on */
1390 printf("LX, LY, hit_mask == %d, %d, %d\n", LX, LY, hit_mask);
1393 if ((hit_mask == HIT_MASK_TOPLEFT ||
1394 hit_mask == HIT_MASK_TOPRIGHT ||
1395 hit_mask == HIT_MASK_BOTTOMLEFT ||
1396 hit_mask == HIT_MASK_BOTTOMRIGHT) &&
1397 laser.current_angle % 4) /* angle is not 90° */
1401 if (hit_mask == HIT_MASK_TOPLEFT)
1406 else if (hit_mask == HIT_MASK_TOPRIGHT)
1411 else if (hit_mask == HIT_MASK_BOTTOMLEFT)
1416 else /* (hit_mask == HIT_MASK_BOTTOMRIGHT) */
1422 AddDamagedField((LX + 2 * dx) / TILEX, (LY + 2 * dy) / TILEY);
1428 printf("[HitOnlyAnEdge() == TRUE]\n");
1435 printf("[HitOnlyAnEdge() == FALSE]\n");
1441 boolean HitPolarizer(int element, int hit_mask)
1443 if (HitOnlyAnEdge(element, hit_mask))
1446 if (IS_DF_GRID(element))
1448 int grid_angle = get_element_angle(element);
1451 printf("HitPolarizer: angle: grid == %d, laser == %d\n",
1452 grid_angle, laser.current_angle);
1455 AddLaserEdge(LX, LY);
1456 AddDamagedField(ELX, ELY);
1459 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1461 if (laser.current_angle == grid_angle ||
1462 laser.current_angle == get_opposite_angle(grid_angle))
1464 /* skip the whole element before continuing the scan */
1470 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1472 if (LX/TILEX > ELX || LY/TILEY > ELY)
1474 /* skipping scan positions to the right and down skips one scan
1475 position too much, because this is only the top left scan position
1476 of totally four scan positions (plus one to the right, one to the
1477 bottom and one to the bottom right) */
1483 AddLaserEdge(LX, LY);
1489 printf("HitPolarizer: LX, LY == %d, %d [%d, %d] [%d, %d]\n",
1491 LX / TILEX, LY / TILEY,
1492 LX % TILEX, LY % TILEY);
1497 else if (IS_GRID_STEEL_FIXED(element) || IS_GRID_STEEL_AUTO(element))
1499 return HitReflectingWalls(element, hit_mask);
1503 return HitAbsorbingWalls(element, hit_mask);
1506 else if (IS_GRID_STEEL(element))
1508 return HitReflectingWalls(element, hit_mask);
1510 else /* IS_GRID_WOOD */
1512 return HitAbsorbingWalls(element, hit_mask);
1518 boolean HitBlock(int element, int hit_mask)
1520 boolean check = FALSE;
1522 if ((element == EL_GATE_STONE || element == EL_GATE_WOOD) &&
1523 game_mm.num_keys == 0)
1526 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1529 int ex = ELX * TILEX + 14;
1530 int ey = ELY * TILEY + 14;
1534 for (i = 1; i < 32; i++)
1539 if ((x == ex || x == ex + 1) && (y == ey || y == ey + 1))
1544 if (check && (element == EL_BLOCK_WOOD || element == EL_GATE_WOOD))
1545 return HitAbsorbingWalls(element, hit_mask);
1549 AddLaserEdge(LX - XS, LY - YS);
1550 AddDamagedField(ELX, ELY);
1553 Box[ELX][ELY] = laser.num_edges;
1555 return HitReflectingWalls(element, hit_mask);
1558 if (element == EL_GATE_STONE || element == EL_GATE_WOOD)
1560 int xs = XS / 2, ys = YS / 2;
1561 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1562 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1564 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1565 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1567 laser.overloaded = (element == EL_GATE_STONE);
1572 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1573 (hit_mask == HIT_MASK_TOP ||
1574 hit_mask == HIT_MASK_LEFT ||
1575 hit_mask == HIT_MASK_RIGHT ||
1576 hit_mask == HIT_MASK_BOTTOM))
1577 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1578 hit_mask == HIT_MASK_BOTTOM),
1579 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1580 hit_mask == HIT_MASK_RIGHT));
1581 AddLaserEdge(LX, LY);
1587 if (element == EL_GATE_STONE && Box[ELX][ELY])
1589 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
1601 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1603 int xs = XS / 2, ys = YS / 2;
1604 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1605 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1607 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1608 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1610 laser.overloaded = (element == EL_BLOCK_STONE);
1615 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1616 (hit_mask == HIT_MASK_TOP ||
1617 hit_mask == HIT_MASK_LEFT ||
1618 hit_mask == HIT_MASK_RIGHT ||
1619 hit_mask == HIT_MASK_BOTTOM))
1620 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1621 hit_mask == HIT_MASK_BOTTOM),
1622 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1623 hit_mask == HIT_MASK_RIGHT));
1624 AddDamagedField(ELX, ELY);
1626 LX = ELX * TILEX + 14;
1627 LY = ELY * TILEY + 14;
1629 AddLaserEdge(LX, LY);
1631 laser.stops_inside_element = TRUE;
1639 boolean HitLaserSource(int element, int hit_mask)
1641 if (HitOnlyAnEdge(element, hit_mask))
1644 PlaySoundStereo(SND_AUTSCH, ST(ELX));
1645 laser.overloaded = TRUE;
1650 boolean HitLaserDestination(int element, int hit_mask)
1652 if (HitOnlyAnEdge(element, hit_mask))
1655 if (element != EL_EXIT_OPEN &&
1656 !(IS_RECEIVER(element) &&
1657 game_mm.kettles_still_needed == 0 &&
1658 laser.current_angle == get_opposite_angle(get_element_angle(element))))
1660 PlaySoundStereo(SND_HOLZ, ST(ELX));
1664 if (IS_RECEIVER(element) ||
1665 (IS_22_5_ANGLE(laser.current_angle) &&
1666 (ELX != (LX + 6 * XS) / TILEX ||
1667 ELY != (LY + 6 * YS) / TILEY ||
1676 LX = ELX * TILEX + 14;
1677 LY = ELY * TILEY + 14;
1679 laser.stops_inside_element = TRUE;
1682 AddLaserEdge(LX, LY);
1683 AddDamagedField(ELX, ELY);
1685 if (game_mm.lights_still_needed == 0)
1686 game_mm.level_solved = TRUE;
1691 boolean HitReflectingWalls(int element, int hit_mask)
1693 /* check if laser hits side of a wall with an angle that is not 90° */
1694 if (!IS_90_ANGLE(laser.current_angle) && (hit_mask == HIT_MASK_TOP ||
1695 hit_mask == HIT_MASK_LEFT ||
1696 hit_mask == HIT_MASK_RIGHT ||
1697 hit_mask == HIT_MASK_BOTTOM))
1699 PlaySoundStereo(SND_HUI, ST(ELX));
1704 if (!IS_DF_GRID(element))
1705 AddLaserEdge(LX, LY);
1707 /* check if laser hits wall with an angle of 45° */
1708 if (!IS_22_5_ANGLE(laser.current_angle))
1710 if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1713 laser.current_angle = get_mirrored_angle(laser.current_angle,
1716 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1719 laser.current_angle = get_mirrored_angle(laser.current_angle,
1723 AddLaserEdge(LX, LY);
1725 XS = 2 * Step[laser.current_angle].x;
1726 YS = 2 * Step[laser.current_angle].y;
1730 else if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1732 laser.current_angle = get_mirrored_angle(laser.current_angle,
1737 if (!IS_DF_GRID(element))
1738 AddLaserEdge(LX, LY);
1743 if (!IS_DF_GRID(element))
1744 AddLaserEdge(LX, LY + YS / 2);
1747 if (!IS_DF_GRID(element))
1748 AddLaserEdge(LX, LY);
1751 YS = 2 * Step[laser.current_angle].y;
1755 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1757 laser.current_angle = get_mirrored_angle(laser.current_angle,
1762 if (!IS_DF_GRID(element))
1763 AddLaserEdge(LX, LY);
1768 if (!IS_DF_GRID(element))
1769 AddLaserEdge(LX + XS / 2, LY);
1772 if (!IS_DF_GRID(element))
1773 AddLaserEdge(LX, LY);
1776 XS = 2 * Step[laser.current_angle].x;
1782 /* reflection at the edge of reflecting DF style wall */
1783 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1785 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1786 hit_mask == HIT_MASK_TOPRIGHT) ||
1787 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1788 hit_mask == HIT_MASK_TOPLEFT) ||
1789 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1790 hit_mask == HIT_MASK_BOTTOMLEFT) ||
1791 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1792 hit_mask == HIT_MASK_BOTTOMRIGHT))
1795 (hit_mask == HIT_MASK_TOPRIGHT || hit_mask == HIT_MASK_BOTTOMLEFT ?
1796 ANG_MIRROR_135 : ANG_MIRROR_45);
1798 PlaySoundStereo(SND_HUI, ST(ELX));
1800 AddDamagedField(ELX, ELY);
1801 AddLaserEdge(LX, LY);
1803 laser.current_angle = get_mirrored_angle(laser.current_angle,
1811 AddLaserEdge(LX, LY);
1817 /* reflection inside an edge of reflecting DF style wall */
1818 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1820 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1821 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT)) ||
1822 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1823 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMRIGHT)) ||
1824 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1825 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT)) ||
1826 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1827 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPLEFT)))
1830 (hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT) ||
1831 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT) ?
1832 ANG_MIRROR_135 : ANG_MIRROR_45);
1834 PlaySoundStereo(SND_HUI, ST(ELX));
1837 AddDamagedField(ELX, ELY);
1840 AddLaserEdge(LX - XS, LY - YS);
1841 AddLaserEdge(LX - XS + (ABS(XS) == 4 ? XS/2 : 0),
1842 LY - YS + (ABS(YS) == 4 ? YS/2 : 0));
1844 laser.current_angle = get_mirrored_angle(laser.current_angle,
1852 AddLaserEdge(LX, LY);
1858 /* check if laser hits DF style wall with an angle of 90° */
1859 if (IS_DF_WALL(element) && IS_90_ANGLE(laser.current_angle))
1861 if ((IS_HORIZ_ANGLE(laser.current_angle) &&
1862 (!(hit_mask & HIT_MASK_TOP) || !(hit_mask & HIT_MASK_BOTTOM))) ||
1863 (IS_VERT_ANGLE(laser.current_angle) &&
1864 (!(hit_mask & HIT_MASK_LEFT) || !(hit_mask & HIT_MASK_RIGHT))))
1866 static int last_LX = 0, last_LY = 0, last_hit_mask = 0;
1868 /* laser at last step touched nothing or the same side of the wall */
1869 if (LX != last_LX || LY != last_LY || hit_mask == last_hit_mask)
1871 AddDamagedField(ELX, ELY);
1878 last_hit_mask = hit_mask;
1885 if (!HitOnlyAnEdge(element, hit_mask))
1887 laser.overloaded = TRUE;
1895 boolean HitAbsorbingWalls(int element, int hit_mask)
1897 if (HitOnlyAnEdge(element, hit_mask))
1901 (hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT))
1903 AddLaserEdge(LX - XS, LY - YS);
1910 (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM))
1912 AddLaserEdge(LX - XS, LY - YS);
1918 if (IS_WALL_WOOD(element) ||
1919 IS_DF_WALL_WOOD(element) ||
1920 IS_GRID_WOOD(element) ||
1921 IS_GRID_WOOD_FIXED(element) ||
1922 IS_GRID_WOOD_AUTO(element) ||
1923 element == EL_FUSE_ON ||
1924 element == EL_BLOCK_WOOD ||
1925 element == EL_GATE_WOOD)
1927 PlaySoundStereo(SND_HOLZ, ST(ELX));
1932 if (IS_WALL_ICE(element))
1936 mask = (LX + XS) / MINI_TILEX - ELX * 2 + 1; /* Quadrant (horizontal) */
1937 mask <<= (((LY + YS) / MINI_TILEY - ELY * 2) > 0) * 2; /* || (vertical) */
1939 /* check if laser hits wall with an angle of 90° */
1940 if (IS_90_ANGLE(laser.current_angle))
1941 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1943 if (mask == 1 || mask == 2 || mask == 4 || mask == 8)
1947 for (i = 0; i < 4; i++)
1949 if (mask == (1 << i) && (XS > 0) == (i % 2) && (YS > 0) == (i / 2))
1950 mask = 15 - (8 >> i);
1951 else if (ABS(XS) == 4 &&
1953 (XS > 0) == (i % 2) &&
1954 (YS < 0) == (i / 2))
1955 mask = 3 + (i / 2) * 9;
1956 else if (ABS(YS) == 4 &&
1958 (XS < 0) == (i % 2) &&
1959 (YS > 0) == (i / 2))
1960 mask = 5 + (i % 2) * 5;
1964 laser.wall_mask = mask;
1966 else if (IS_WALL_AMOEBA(element))
1968 int elx = (LX - 2 * XS) / TILEX;
1969 int ely = (LY - 2 * YS) / TILEY;
1970 int element2 = Feld[elx][ely];
1973 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element2))
1975 laser.dest_element = EL_EMPTY;
1983 mask = (LX - 2 * XS) / 16 - ELX * 2 + 1;
1984 mask <<= ((LY - 2 * YS) / 16 - ELY * 2) * 2;
1986 if (IS_90_ANGLE(laser.current_angle))
1987 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1989 laser.dest_element = element2 | EL_WALL_AMOEBA;
1991 laser.wall_mask = mask;
1997 void OpenExit(int x, int y)
2001 if (!MovDelay[x][y]) /* next animation frame */
2002 MovDelay[x][y] = 4 * delay;
2004 if (MovDelay[x][y]) /* wait some time before next frame */
2009 phase = MovDelay[x][y] / delay;
2011 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2012 DrawGraphicAnimation_MM(x, y, IMG_MM_EXIT_OPENING, 3 - phase);
2014 if (!MovDelay[x][y])
2016 Feld[x][y] = EL_EXIT_OPEN;
2022 void OpenSurpriseBall(int x, int y)
2026 if (!MovDelay[x][y]) /* next animation frame */
2027 MovDelay[x][y] = 50 * delay;
2029 if (MovDelay[x][y]) /* wait some time before next frame */
2033 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2036 int graphic = el2gfx(Store[x][y]);
2038 int dx = RND(26), dy = RND(26);
2040 getGraphicSource(graphic, 0, &bitmap, &gx, &gy);
2042 BlitBitmap(bitmap, drawto, gx + dx, gy + dy, 6, 6,
2043 SX + x * TILEX + dx, SY + y * TILEY + dy);
2045 MarkTileDirty(x, y);
2048 if (!MovDelay[x][y])
2050 Feld[x][y] = Store[x][y];
2059 void MeltIce(int x, int y)
2064 if (!MovDelay[x][y]) /* next animation frame */
2065 MovDelay[x][y] = frames * delay;
2067 if (MovDelay[x][y]) /* wait some time before next frame */
2070 int wall_mask = Store2[x][y];
2071 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_ICE;
2074 phase = frames - MovDelay[x][y] / delay - 1;
2076 if (!MovDelay[x][y])
2080 Feld[x][y] = real_element & (wall_mask ^ 0xFF);
2081 Store[x][y] = Store2[x][y] = 0;
2083 DrawWalls_MM(x, y, Feld[x][y]);
2085 if (Feld[x][y] == EL_WALL_ICE)
2086 Feld[x][y] = EL_EMPTY;
2088 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
2089 if (laser.damage[i].is_mirror)
2093 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
2095 DrawLaser(0, DL_LASER_DISABLED);
2099 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2101 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2103 laser.redraw = TRUE;
2108 void GrowAmoeba(int x, int y)
2113 if (!MovDelay[x][y]) /* next animation frame */
2114 MovDelay[x][y] = frames * delay;
2116 if (MovDelay[x][y]) /* wait some time before next frame */
2119 int wall_mask = Store2[x][y];
2120 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_AMOEBA;
2123 phase = MovDelay[x][y] / delay;
2125 if (!MovDelay[x][y])
2127 Feld[x][y] = real_element;
2128 Store[x][y] = Store2[x][y] = 0;
2130 DrawWalls_MM(x, y, Feld[x][y]);
2131 DrawLaser(0, DL_LASER_ENABLED);
2133 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2135 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2140 static void Explode_MM(int x, int y, int phase, int mode)
2142 int num_phase = 9, delay = 2;
2143 int last_phase = num_phase * delay;
2144 int half_phase = (num_phase / 2) * delay;
2146 laser.redraw = TRUE;
2148 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2150 int center_element = Feld[x][y];
2152 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2154 /* put moving element to center field (and let it explode there) */
2155 center_element = MovingOrBlocked2Element_MM(x, y);
2156 RemoveMovingField_MM(x, y);
2158 Feld[x][y] = center_element;
2161 if (center_element == EL_BOMB || IS_MCDUFFIN(center_element))
2162 Store[x][y] = center_element;
2164 Store[x][y] = EL_EMPTY;
2166 Store2[x][y] = mode;
2167 Feld[x][y] = EL_EXPLODING_OPAQUE;
2168 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2174 Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
2176 if (phase == half_phase)
2178 Feld[x][y] = EL_EXPLODING_TRANSP;
2180 if (x == ELX && y == ELY)
2184 if (phase == last_phase)
2186 if (Store[x][y] == EL_BOMB)
2188 laser.num_damages--;
2189 DrawLaser(0, DL_LASER_DISABLED);
2190 laser.num_edges = 0;
2192 Bang_MM(laser.start_edge.x, laser.start_edge.y);
2193 Store[x][y] = EL_EMPTY;
2195 else if (IS_MCDUFFIN(Store[x][y]))
2197 game_mm.game_over = TRUE;
2198 game_mm.game_over_cause = GAME_OVER_BOMB;
2199 Store[x][y] = EL_EMPTY;
2202 Feld[x][y] = Store[x][y];
2203 Store[x][y] = Store2[x][y] = 0;
2204 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2206 InitField(x, y, FALSE);
2209 else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2211 int graphic = IMG_MM_DEFAULT_EXPLODING;
2212 int graphic_phase = (phase / delay - 1);
2216 if (Store2[x][y] == EX_KETTLE)
2218 if (graphic_phase < 3)
2220 graphic = IMG_MM_KETTLE_EXPLODING;
2222 else if (graphic_phase < 5)
2228 graphic = IMG_EMPTY;
2232 else if (Store2[x][y] == EX_SHORT)
2234 if (graphic_phase < 4)
2240 graphic = IMG_EMPTY;
2245 getGraphicSource(graphic, graphic_phase, &bitmap, &src_x, &src_y);
2247 BlitBitmap(bitmap, drawto_field, src_x, src_y, TILEX, TILEY,
2248 FX + x * TILEX, FY + y * TILEY);
2250 MarkTileDirty(x, y);
2254 static void Bang_MM(int x, int y)
2256 int element = Feld[x][y];
2257 int mode = EX_NORMAL;
2260 DrawLaser(0, DL_LASER_ENABLED);
2279 if (IS_PACMAN(element))
2280 PlaySoundStereo(SND_QUIEK, ST(x));
2281 else if (element == EL_BOMB || IS_MCDUFFIN(element))
2282 PlaySoundStereo(SND_ROAAAR, ST(x));
2283 else if (element == EL_KEY)
2284 PlaySoundStereo(SND_KLING, ST(x));
2286 PlaySoundStereo((mode == EX_SHORT ? SND_WHOOSH : SND_KABUMM), ST(x));
2288 Explode_MM(x, y, EX_PHASE_START, mode);
2291 void TurnRound(int x, int y)
2303 { 0, 0 }, { 0, 0 }, { 0, 0 },
2308 int left, right, back;
2312 { MV_DOWN, MV_UP, MV_RIGHT },
2313 { MV_UP, MV_DOWN, MV_LEFT },
2315 { MV_LEFT, MV_RIGHT, MV_DOWN },
2316 { 0,0,0 }, { 0,0,0 }, { 0,0,0 },
2317 { MV_RIGHT, MV_LEFT, MV_UP }
2320 int element = Feld[x][y];
2321 int old_move_dir = MovDir[x][y];
2322 int right_dir = turn[old_move_dir].right;
2323 int back_dir = turn[old_move_dir].back;
2324 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
2325 int right_x = x + right_dx, right_y = y + right_dy;
2327 if (element == EL_PACMAN)
2329 boolean can_turn_right = FALSE;
2331 if (IN_LEV_FIELD(right_x, right_y) &&
2332 IS_EATABLE4PACMAN(Feld[right_x][right_y]))
2333 can_turn_right = TRUE;
2336 MovDir[x][y] = right_dir;
2338 MovDir[x][y] = back_dir;
2344 static void StartMoving_MM(int x, int y)
2346 int element = Feld[x][y];
2351 if (CAN_MOVE(element))
2355 if (MovDelay[x][y]) /* wait some time before next movement */
2363 /* now make next step */
2365 Moving2Blocked_MM(x, y, &newx, &newy); /* get next screen position */
2367 if (element == EL_PACMAN &&
2368 IN_LEV_FIELD(newx, newy) && IS_EATABLE4PACMAN(Feld[newx][newy]) &&
2369 !ObjHit(newx, newy, HIT_POS_CENTER))
2371 Store[newx][newy] = Feld[newx][newy];
2372 Feld[newx][newy] = EL_EMPTY;
2374 DrawField_MM(newx, newy);
2376 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy) ||
2377 ObjHit(newx, newy, HIT_POS_CENTER))
2379 /* object was running against a wall */
2386 InitMovingField_MM(x, y, MovDir[x][y]);
2390 ContinueMoving_MM(x, y);
2393 static void ContinueMoving_MM(int x, int y)
2395 int element = Feld[x][y];
2396 int direction = MovDir[x][y];
2397 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2398 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2399 int horiz_move = (dx!=0);
2400 int newx = x + dx, newy = y + dy;
2401 int step = (horiz_move ? dx : dy) * TILEX / 8;
2403 MovPos[x][y] += step;
2405 if (ABS(MovPos[x][y]) >= TILEX) /* object reached its destination */
2407 Feld[x][y] = EL_EMPTY;
2408 Feld[newx][newy] = element;
2410 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2411 MovDelay[newx][newy] = 0;
2413 if (!CAN_MOVE(element))
2414 MovDir[newx][newy] = 0;
2417 DrawField_MM(newx, newy);
2419 Stop[newx][newy] = TRUE;
2421 if (element == EL_PACMAN)
2423 if (Store[newx][newy] == EL_BOMB)
2424 Bang_MM(newx, newy);
2426 if (IS_WALL_AMOEBA(Store[newx][newy]) &&
2427 (LX + 2 * XS) / TILEX == newx &&
2428 (LY + 2 * YS) / TILEY == newy)
2435 else /* still moving on */
2440 laser.redraw = TRUE;
2443 void ClickElement(int mx, int my, int button)
2445 static unsigned int click_delay = 0;
2446 static int click_delay_value = CLICK_DELAY_SHORT;
2447 static boolean new_button = TRUE;
2449 int x = (mx - SX) / TILEX, y = (my - SY) / TILEY;
2451 /* do not rotate objects hit by the laser after the game was solved */
2452 if (game_mm.level_solved && Hit[x][y])
2455 if (button == MB_RELEASED)
2458 click_delay_value = CLICK_DELAY_SHORT;
2460 /* release eventually hold auto-rotating mirror */
2461 RotateMirror(x, y, MB_RELEASED);
2466 if (!DelayReached(&click_delay, click_delay_value) && !new_button)
2469 if (button == MB_MIDDLEBUTTON) /* middle button has no function */
2472 if (!IN_PIX_FIELD(mx - SX, my - SY))
2475 if (Feld[x][y] == EL_EMPTY)
2478 element = Feld[x][y];
2480 if (IS_MIRROR(element) ||
2481 IS_BEAMER(element) ||
2482 IS_POLAR(element) ||
2483 IS_POLAR_CROSS(element) ||
2484 IS_DF_MIRROR(element) ||
2485 IS_DF_MIRROR_AUTO(element))
2487 RotateMirror(x, y, button);
2489 else if (IS_MCDUFFIN(element))
2491 if (!laser.fuse_off)
2493 DrawLaser(0, DL_LASER_DISABLED);
2500 element = get_rotated_element(element, BUTTON_ROTATION(button));
2501 laser.start_angle = get_element_angle(element);
2505 Feld[x][y] = element;
2512 if (!laser.fuse_off)
2515 else if (element == EL_FUSE_ON && laser.fuse_off)
2517 if (x != laser.fuse_x || y != laser.fuse_y)
2520 laser.fuse_off = FALSE;
2521 laser.fuse_x = laser.fuse_y = -1;
2523 DrawGraphic_MM(x, y, IMG_MM_FUSE_ACTIVE);
2526 else if (element == EL_FUSE_ON && !laser.fuse_off && new_button)
2528 laser.fuse_off = TRUE;
2531 laser.overloaded = FALSE;
2533 DrawLaser(0, DL_LASER_DISABLED);
2534 DrawGraphic_MM(x, y, IMG_MM_FUSE);
2536 else if (element == EL_LIGHTBALL)
2540 DrawLaser(0, DL_LASER_ENABLED);
2543 click_delay_value = (new_button ? CLICK_DELAY_LONG : CLICK_DELAY_SHORT);
2547 void RotateMirror(int x, int y, int button)
2549 static int hold_x = -1, hold_y = -1;
2551 if (button == MB_RELEASED)
2553 /* release eventually hold auto-rotating mirror */
2560 if (IS_MIRROR(Feld[x][y]) ||
2561 IS_POLAR_CROSS(Feld[x][y]) ||
2562 IS_POLAR(Feld[x][y]) ||
2563 IS_BEAMER(Feld[x][y]) ||
2564 IS_DF_MIRROR(Feld[x][y]) ||
2565 IS_GRID_STEEL_AUTO(Feld[x][y]) ||
2566 IS_GRID_WOOD_AUTO(Feld[x][y]))
2568 Feld[x][y] = get_rotated_element(Feld[x][y], BUTTON_ROTATION(button));
2570 else if (IS_DF_MIRROR_AUTO(Feld[x][y]))
2572 if (button == MB_LEFTBUTTON)
2574 /* left mouse button only for manual adjustment, no auto-rotating;
2575 freeze mirror for until mouse button released */
2579 else if (button == MB_RIGHTBUTTON && (hold_x != x || hold_y != y))
2581 Feld[x][y] = get_rotated_element(Feld[x][y], ROTATE_RIGHT);
2585 if (IS_GRID_STEEL_AUTO(Feld[x][y]) || IS_GRID_WOOD_AUTO(Feld[x][y]))
2587 int edge = Hit[x][y];
2593 DrawLaser(edge - 1, DL_LASER_DISABLED);
2597 else if (ObjHit(x, y, HIT_POS_CENTER))
2599 int edge = Hit[x][y];
2603 Error(ERR_WARN, "RotateMirror: inconsistent field Hit[][]!\n");
2607 DrawLaser(edge - 1, DL_LASER_DISABLED);
2614 if (ObjHit(x, y, HIT_POS_EDGE | HIT_POS_BETWEEN))
2619 if ((IS_BEAMER(Feld[x][y]) ||
2620 IS_POLAR(Feld[x][y]) ||
2621 IS_POLAR_CROSS(Feld[x][y])) && x == ELX && y == ELY)
2625 if (IS_BEAMER(Feld[x][y]))
2628 printf("TEST (%d, %d) [%d] [%d]\n",
2630 laser.beamer_edge, laser.beamer[1].num);
2640 DrawLaser(0, DL_LASER_ENABLED);
2644 void AutoRotateMirrors()
2646 static unsigned int rotate_delay = 0;
2649 if (!DelayReached(&rotate_delay, AUTO_ROTATE_DELAY))
2652 for (x = 0; x < lev_fieldx; x++)
2654 for (y = 0; y < lev_fieldy; y++)
2656 int element = Feld[x][y];
2658 /* do not rotate objects hit by the laser after the game was solved */
2659 if (game_mm.level_solved && Hit[x][y])
2662 if (IS_DF_MIRROR_AUTO(element) ||
2663 IS_GRID_WOOD_AUTO(element) ||
2664 IS_GRID_STEEL_AUTO(element) ||
2665 element == EL_REFRACTOR)
2666 RotateMirror(x, y, MB_RIGHTBUTTON);
2671 boolean ObjHit(int obx, int oby, int bits)
2678 if (bits & HIT_POS_CENTER)
2680 if (ReadPixel(drawto, SX + obx + 15, SY + oby + 15) == pen_ray)
2684 if (bits & HIT_POS_EDGE)
2686 for (i = 0; i < 4; i++)
2687 if (ReadPixel(drawto,
2688 SX + obx + 31 * (i % 2),
2689 SY + oby + 31 * (i / 2)) == pen_ray)
2693 if (bits & HIT_POS_BETWEEN)
2695 for (i = 0; i < 4; i++)
2696 if (ReadPixel(drawto,
2697 SX + 4 + obx + 22 * (i % 2),
2698 SY + 4 + oby + 22 * (i / 2)) == pen_ray)
2705 void DeletePacMan(int px, int py)
2711 if (game_mm.num_pacman <= 1)
2713 game_mm.num_pacman = 0;
2717 for (i = 0; i < game_mm.num_pacman; i++)
2718 if (game_mm.pacman[i].x == px && game_mm.pacman[i].y == py)
2721 game_mm.num_pacman--;
2723 for (j = i; j < game_mm.num_pacman; j++)
2725 game_mm.pacman[j].x = game_mm.pacman[j + 1].x;
2726 game_mm.pacman[j].y = game_mm.pacman[j + 1].y;
2727 game_mm.pacman[j].dir = game_mm.pacman[j + 1].dir;
2731 void ColorCycling(void)
2733 static int CC, Cc = 0;
2735 static int color, old = 0xF00, new = 0x010, mult = 1;
2736 static unsigned short red, green, blue;
2738 if (color_status == STATIC_COLORS)
2743 if (CC < Cc || CC > Cc + 50)
2747 color = old + new * mult;
2753 if (ABS(mult) == 16)
2763 red = 0x0e00 * ((color & 0xF00) >> 8);
2764 green = 0x0e00 * ((color & 0x0F0) >> 4);
2765 blue = 0x0e00 * ((color & 0x00F));
2766 SetRGB(pen_magicolor[0], red, green, blue);
2768 red = 0x1111 * ((color & 0xF00) >> 8);
2769 green = 0x1111 * ((color & 0x0F0) >> 4);
2770 blue = 0x1111 * ((color & 0x00F));
2771 SetRGB(pen_magicolor[1], red, green, blue);
2775 static void GameActions_MM_Ext(byte action[MAX_PLAYERS], boolean warp_mode)
2777 static unsigned int action_delay = 0;
2778 static unsigned int pacman_delay = 0;
2779 static unsigned int energy_delay = 0;
2780 static unsigned int overload_delay = 0;
2786 WaitUntilDelayReached(&action_delay, GameFrameDelay);
2788 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2791 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2793 element = Feld[x][y];
2795 if (!IS_MOVING(x, y) && CAN_MOVE(element))
2796 StartMoving_MM(x, y);
2797 else if (IS_MOVING(x, y))
2798 ContinueMoving_MM(x, y);
2799 else if (IS_EXPLODING(element))
2800 Explode_MM(x, y, Frame[x][y], EX_NORMAL);
2801 else if (element == EL_EXIT_OPENING)
2803 else if (element == EL_GRAY_BALL_OPENING)
2804 OpenSurpriseBall(x, y);
2805 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_ICE)
2807 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_AMOEBA)
2811 AutoRotateMirrors();
2814 /* !!! CHANGE THIS: REDRAW ONLY WHEN NEEDED !!! */
2816 /* redraw after Explode_MM() ... */
2818 DrawLaser(0, DL_LASER_ENABLED);
2819 laser.redraw = FALSE;
2824 if (game_mm.num_pacman && DelayReached(&pacman_delay, 250))
2828 if (laser.num_damages > MAX_LASER_LEN && !laser.fuse_off)
2830 DrawLaser(0, DL_LASER_DISABLED);
2835 if (DelayReached(&energy_delay, 4000))
2837 game_mm.energy_left--;
2838 if (game_mm.energy_left >= 0)
2841 BlitBitmap(pix[PIX_DOOR], drawto,
2842 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
2843 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
2844 DX_ENERGY, DY_ENERGY);
2846 redraw_mask |= REDRAW_DOOR_1;
2848 else if (setup.time_limit)
2852 for (i = 15; i >= 0; i--)
2855 SetRGB(pen_ray, 0x0000, 0x0000, i * color_scale);
2857 pen_ray = GetPixelFromRGB(window,
2858 native_mm_level.laser_red * 0x11 * i,
2859 native_mm_level.laser_green * 0x11 * i,
2860 native_mm_level.laser_blue * 0x11 * i);
2862 DrawLaser(0, DL_LASER_ENABLED);
2867 StopSound(SND_WARNTON);
2870 DrawLaser(0, DL_LASER_DISABLED);
2871 game_mm.game_over = TRUE;
2872 game_mm.game_over_cause = GAME_OVER_NO_ENERGY;
2875 if (Request("Out of magic energy ! Play it again ?",
2876 REQ_ASK | REQ_STAY_CLOSED))
2882 game_status = MAINMENU;
2891 element = laser.dest_element;
2894 if (element != Feld[ELX][ELY])
2896 printf("element == %d, Feld[ELX][ELY] == %d\n",
2897 element, Feld[ELX][ELY]);
2901 if (!laser.overloaded && laser.overload_value == 0 &&
2902 element != EL_BOMB &&
2903 element != EL_MINE &&
2904 element != EL_BALL_GRAY &&
2905 element != EL_BLOCK_STONE &&
2906 element != EL_BLOCK_WOOD &&
2907 element != EL_FUSE_ON &&
2908 element != EL_FUEL_FULL &&
2909 !IS_WALL_ICE(element) &&
2910 !IS_WALL_AMOEBA(element))
2913 if (((laser.overloaded && laser.overload_value < MAX_LASER_OVERLOAD) ||
2914 (!laser.overloaded && laser.overload_value > 0)) &&
2915 DelayReached(&overload_delay, 60 + !laser.overloaded * 120))
2917 if (laser.overloaded)
2918 laser.overload_value++;
2920 laser.overload_value--;
2922 if (game_mm.cheat_no_overload)
2924 laser.overloaded = FALSE;
2925 laser.overload_value = 0;
2928 if (laser.overload_value < MAX_LASER_OVERLOAD - 8)
2930 int color_up = 0xFF * laser.overload_value / MAX_LASER_OVERLOAD;
2931 int color_down = 0xFF - color_up;
2934 SetRGB(pen_ray, (laser.overload_value / 6) * color_scale, 0x0000,
2935 (15 - (laser.overload_value / 6)) * color_scale);
2938 GetPixelFromRGB(window,
2939 (native_mm_level.laser_red ? 0xFF : color_up),
2940 (native_mm_level.laser_green ? color_down : 0x00),
2941 (native_mm_level.laser_blue ? color_down : 0x00));
2943 DrawLaser(0, DL_LASER_ENABLED);
2947 if (laser.overloaded)
2949 if (setup.sound_loops)
2950 PlaySoundExt(SND_WARNTON, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
2951 SND_CTRL_PLAY_LOOP);
2953 PlaySoundStereo(SND_WARNTON, SOUND_MAX_RIGHT);
2956 if (!laser.overloaded)
2957 StopSound(SND_WARNTON);
2959 if (laser.overloaded)
2962 BlitBitmap(pix[PIX_DOOR], drawto,
2963 DOOR_GFX_PAGEX4 + XX_OVERLOAD,
2964 DOOR_GFX_PAGEY1 + YY_OVERLOAD + OVERLOAD_YSIZE
2965 - laser.overload_value,
2966 OVERLOAD_XSIZE, laser.overload_value,
2967 DX_OVERLOAD, DY_OVERLOAD + OVERLOAD_YSIZE
2968 - laser.overload_value);
2970 redraw_mask |= REDRAW_DOOR_1;
2975 BlitBitmap(pix[PIX_DOOR], drawto,
2976 DOOR_GFX_PAGEX5 + XX_OVERLOAD, DOOR_GFX_PAGEY1 + YY_OVERLOAD,
2977 OVERLOAD_XSIZE, OVERLOAD_YSIZE - laser.overload_value,
2978 DX_OVERLOAD, DY_OVERLOAD);
2980 redraw_mask |= REDRAW_DOOR_1;
2983 if (laser.overload_value == MAX_LASER_OVERLOAD)
2987 for (i = 15; i >= 0; i--)
2990 SetRGB(pen_ray, i * color_scale, 0x0000, 0x0000);
2993 pen_ray = GetPixelFromRGB(window, 0x11 * i, 0x00, 0x00);
2995 DrawLaser(0, DL_LASER_ENABLED);
3000 DrawLaser(0, DL_LASER_DISABLED);
3002 game_mm.game_over = TRUE;
3003 game_mm.game_over_cause = GAME_OVER_OVERLOADED;
3006 if (Request("Magic spell hit Mc Duffin ! Play it again ?",
3007 REQ_ASK | REQ_STAY_CLOSED))
3013 game_status = MAINMENU;
3027 if (element == EL_BOMB && CT > 1500)
3029 if (game_mm.cheat_no_explosion)
3033 laser.num_damages--;
3034 DrawLaser(0, DL_LASER_DISABLED);
3035 laser.num_edges = 0;
3040 laser.dest_element = EL_EXPLODING_OPAQUE;
3044 laser.num_damages--;
3045 DrawLaser(0, DL_LASER_DISABLED);
3047 laser.num_edges = 0;
3048 Bang_MM(laser.start_edge.x, laser.start_edge.y);
3050 if (Request("Bomb killed Mc Duffin ! Play it again ?",
3051 REQ_ASK | REQ_STAY_CLOSED))
3057 game_status = MAINMENU;
3065 if (element == EL_FUSE_ON && CT > 500)
3067 laser.fuse_off = TRUE;
3071 DrawLaser(0, DL_LASER_DISABLED);
3072 DrawGraphic_MM(ELX, ELY, IMG_MM_FUSE);
3075 if (element == EL_BALL_GRAY && CT > 1500)
3077 static int new_elements[] =
3080 EL_MIRROR_FIXED_START,
3082 EL_POLAR_CROSS_START,
3088 int num_new_elements = sizeof(new_elements) / sizeof(int);
3089 int new_element = new_elements[RND(num_new_elements)];
3091 Store[ELX][ELY] = new_element + RND(get_num_elements(new_element));
3092 Feld[ELX][ELY] = EL_GRAY_BALL_OPENING;
3094 /* !!! CHECK AGAIN: Laser on Polarizer !!! */
3105 element = EL_MIRROR_START + RND(16);
3111 element = (rnd == 0 ? EL_KETTLE : rnd == 1 ? EL_BOMB : EL_PRISM);
3118 element = (rnd == 0 ? EL_FUSE_ON :
3119 rnd >= 1 && rnd <= 4 ? EL_PACMAN_RIGHT + rnd - 1 :
3120 rnd >= 5 && rnd <= 20 ? EL_POLAR_START + rnd - 5 :
3121 rnd >= 21 && rnd <= 24 ? EL_POLAR_CROSS_START + rnd - 21 :
3122 EL_MIRROR_FIXED_START + rnd - 25);
3127 graphic = el2gfx(element);
3129 for (i = 0; i < 50; i++)
3135 BlitBitmap(pix[PIX_BACK], drawto,
3136 SX + (graphic % GFX_PER_LINE) * TILEX + x,
3137 SY + (graphic / GFX_PER_LINE) * TILEY + y, 6, 6,
3138 SX + ELX * TILEX + x,
3139 SY + ELY * TILEY + y);
3141 MarkTileDirty(ELX, ELY);
3144 DrawLaser(0, DL_LASER_ENABLED);
3149 Feld[ELX][ELY] = element;
3150 DrawField_MM(ELX, ELY);
3153 printf("NEW ELEMENT: (%d, %d)\n", ELX, ELY);
3156 /* above stuff: GRAY BALL -> PRISM !!! */
3158 LX = ELX * TILEX + 14;
3159 LY = ELY * TILEY + 14;
3160 if (laser.current_angle == (laser.current_angle >> 1) << 1)
3167 laser.num_edges -= 2;
3168 laser.num_damages--;
3172 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
3173 if (laser.damage[i].is_mirror)
3177 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3179 DrawLaser(0, DL_LASER_DISABLED);
3181 DrawLaser(0, DL_LASER_DISABLED);
3187 printf("TEST ELEMENT: %d\n", Feld[0][0]);
3194 if (IS_WALL_ICE(element) && CT > 1000)
3196 PlaySoundStereo(SND_SLURP, ST(ELX));
3199 Feld[ELX][ELY] = Feld[ELX][ELY] - EL_WALL_ICE + EL_WALL_CHANGING;
3200 Store[ELX][ELY] = EL_WALL_ICE;
3201 Store2[ELX][ELY] = laser.wall_mask;
3203 laser.dest_element = Feld[ELX][ELY];
3208 for (i = 0; i < 5; i++)
3214 Feld[ELX][ELY] &= (laser.wall_mask ^ 0xFF);
3218 DrawWallsAnimation_MM(ELX, ELY, Feld[ELX][ELY], phase, laser.wall_mask);
3223 if (Feld[ELX][ELY] == EL_WALL_ICE)
3224 Feld[ELX][ELY] = EL_EMPTY;
3228 LX = laser.edge[laser.num_edges].x - (SX + 2);
3229 LY = laser.edge[laser.num_edges].y - (SY + 2);
3232 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
3233 if (laser.damage[i].is_mirror)
3237 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3239 DrawLaser(0, DL_LASER_DISABLED);
3246 if (IS_WALL_AMOEBA(element) && CT > 1200)
3248 int k1, k2, k3, dx, dy, de, dm;
3249 int element2 = Feld[ELX][ELY];
3251 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element))
3254 for (i = laser.num_damages - 1; i >= 0; i--)
3255 if (laser.damage[i].is_mirror)
3258 r = laser.num_edges;
3259 d = laser.num_damages;
3266 DrawLaser(laser.damage[k1].edge - 1, DL_LASER_DISABLED);
3269 DrawLaser(0, DL_LASER_ENABLED);
3272 x = laser.damage[k1].x;
3273 y = laser.damage[k1].y;
3278 for (i = 0; i < 4; i++)
3280 if (laser.wall_mask & (1 << i))
3282 if (ReadPixel(drawto,
3283 SX + ELX * TILEX + 14 + (i % 2) * 2,
3284 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3286 if (ReadPixel(drawto,
3287 SX + ELX * TILEX + 31 * (i % 2),
3288 SY + ELY * TILEY + 14 + (i / 2) * 2) == pen_ray)
3295 for (i = 0; i < 4; i++)
3297 if (laser.wall_mask & (1 << i))
3299 if (ReadPixel(drawto,
3300 SX + ELX * TILEX + 31 * (i % 2),
3301 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3308 if (laser.num_beamers > 0 ||
3309 k1 < 1 || k2 < 4 || k3 < 4 ||
3310 ReadPixel(drawto, SX + ELX * TILEX + 14, SY + ELY * TILEY + 14)
3313 laser.num_edges = r;
3314 laser.num_damages = d;
3316 DrawLaser(0, DL_LASER_DISABLED);
3319 Feld[ELX][ELY] = element | laser.wall_mask;
3323 de = Feld[ELX][ELY];
3324 dm = laser.wall_mask;
3328 int x = ELX, y = ELY;
3329 int wall_mask = laser.wall_mask;
3332 DrawLaser(0, DL_LASER_ENABLED);
3334 PlaySoundStereo(SND_AMOEBE, ST(dx));
3336 Feld[x][y] = Feld[x][y] - EL_WALL_AMOEBA + EL_WALL_CHANGING;
3337 Store[x][y] = EL_WALL_AMOEBA;
3338 Store2[x][y] = wall_mask;
3344 DrawWallsAnimation_MM(dx, dy, de, 4, dm);
3346 DrawLaser(0, DL_LASER_ENABLED);
3348 PlaySoundStereo(SND_AMOEBE, ST(dx));
3350 for (i = 4; i >= 0; i--)
3352 DrawWallsAnimation_MM(dx, dy, de, i, dm);
3358 DrawLaser(0, DL_LASER_ENABLED);
3363 if ((element == EL_BLOCK_WOOD || element == EL_BLOCK_STONE) &&
3364 laser.stops_inside_element && CT > 1500)
3369 if (ABS(XS) > ABS(YS))
3376 for (i = 0; i < 4; i++)
3383 x = ELX + Step[k * 4].x;
3384 y = ELY + Step[k * 4].y;
3386 if (!IN_LEV_FIELD(x, y) || Feld[x][y] != EL_EMPTY)
3389 if (ObjHit(x, y, HIT_POS_CENTER | HIT_POS_EDGE | HIT_POS_BETWEEN))
3397 laser.overloaded = (element == EL_BLOCK_STONE);
3402 PlaySoundStereo(SND_BONG, ST(ELX));
3405 Feld[x][y] = element;
3407 DrawGraphic_MM(ELX, ELY, IMG_EMPTY);
3410 if (element == EL_BLOCK_STONE && Box[ELX][ELY])
3412 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
3413 DrawLaser(laser.num_edges - 1, DL_LASER_ENABLED);
3421 if (element == EL_FUEL_FULL && CT > 200)
3423 for (i = game_mm.energy_left; i <= MAX_LASER_ENERGY; i+=2)
3426 BlitBitmap(pix[PIX_DOOR], drawto,
3427 DOOR_GFX_PAGEX4 + XX_ENERGY,
3428 DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
3429 ENERGY_XSIZE, i, DX_ENERGY,
3430 DY_ENERGY + ENERGY_YSIZE - i);
3433 redraw_mask |= REDRAW_DOOR_1;
3439 game_mm.energy_left = MAX_LASER_ENERGY;
3440 Feld[ELX][ELY] = EL_FUEL_EMPTY;
3441 DrawField_MM(ELX, ELY);
3443 DrawLaser(0, DL_LASER_ENABLED);
3451 void GameActions_MM(byte action[MAX_PLAYERS], boolean warp_mode)
3454 ClickElement(0, 0, MB_NOT_PRESSED);
3456 GameActions_MM_Ext(action, warp_mode);
3462 int mx, my, ox, oy, nx, ny;
3466 if (++p >= game_mm.num_pacman)
3469 game_mm.pacman[p].dir--;
3471 for (l = 1; l < 5; l++)
3473 game_mm.pacman[p].dir++;
3475 if (game_mm.pacman[p].dir > 4)
3476 game_mm.pacman[p].dir = 1;
3478 if (game_mm.pacman[p].dir % 2)
3481 my = game_mm.pacman[p].dir - 2;
3486 mx = 3 - game_mm.pacman[p].dir;
3489 ox = game_mm.pacman[p].x;
3490 oy = game_mm.pacman[p].y;
3493 element = Feld[nx][ny];
3495 if (nx < 0 || nx > 15 || ny < 0 || ny > 11)
3498 if (!IS_EATABLE4PACMAN(element))
3501 if (ObjHit(nx, ny, HIT_POS_CENTER))
3504 Feld[ox][oy] = EL_EMPTY;
3506 EL_PACMAN_RIGHT - 1 +
3507 (game_mm.pacman[p].dir - 1 +
3508 (game_mm.pacman[p].dir % 2) * 2);
3510 game_mm.pacman[p].x = nx;
3511 game_mm.pacman[p].y = ny;
3513 DrawGraphic_MM(ox, oy, IMG_EMPTY);
3515 if (element != EL_EMPTY)
3517 int graphic = el2gfx(Feld[nx][ny]);
3522 getGraphicSource(graphic, 0, &bitmap, &src_x, &src_y);
3525 ox = SX + ox * TILEX;
3526 oy = SY + oy * TILEY;
3528 for (i = 1; i < 33; i += 2)
3529 BlitBitmap(bitmap, window,
3530 src_x, src_y, TILEX, TILEY,
3531 ox + i * mx, oy + i * my);
3532 Ct = Ct + Counter() - CT;
3535 DrawField_MM(nx, ny);
3538 if (!laser.fuse_off)
3540 DrawLaser(0, DL_LASER_ENABLED);
3542 if (ObjHit(nx, ny, HIT_POS_BETWEEN))
3544 AddDamagedField(nx, ny);
3546 laser.damage[laser.num_damages - 1].edge = 0;
3550 if (element == EL_BOMB)
3551 DeletePacMan(nx, ny);
3553 if (IS_WALL_AMOEBA(element) &&
3554 (LX + 2 * XS) / TILEX == nx &&
3555 (LY + 2 * YS) / TILEY == ny)
3568 boolean raise_level = FALSE;
3571 if (local_player->MovPos)
3574 local_player->LevelSolved = FALSE;
3577 if (game_mm.energy_left)
3579 if (setup.sound_loops)
3580 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3581 SND_CTRL_PLAY_LOOP);
3583 while (game_mm.energy_left > 0)
3585 if (!setup.sound_loops)
3586 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3589 if (game_mm.energy_left > 0 && !(game_mm.energy_left % 10))
3590 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3595 game_mm.energy_left--;
3596 if (game_mm.energy_left >= 0)
3599 BlitBitmap(pix[PIX_DOOR], drawto,
3600 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
3601 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
3602 DX_ENERGY, DY_ENERGY);
3604 redraw_mask |= REDRAW_DOOR_1;
3611 if (setup.sound_loops)
3612 StopSound(SND_SIRR);
3614 else if (native_mm_level.time == 0) /* level without time limit */
3616 if (setup.sound_loops)
3617 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3618 SND_CTRL_PLAY_LOOP);
3620 while (TimePlayed < 999)
3622 if (!setup.sound_loops)
3623 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3624 if (TimePlayed < 999 && !(TimePlayed % 10))
3625 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3626 if (TimePlayed < 900 && !(TimePlayed % 10))
3632 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
3639 if (setup.sound_loops)
3640 StopSound(SND_SIRR);
3647 CloseDoor(DOOR_CLOSE_1);
3649 Request("Level solved !", REQ_CONFIRM);
3651 if (level_nr == leveldir_current->handicap_level)
3653 leveldir_current->handicap_level++;
3654 SaveLevelSetup_SeriesInfo();
3657 if (level_editor_test_game)
3658 game_mm.score = -1; /* no highscore when playing from editor */
3659 else if (level_nr < leveldir_current->last_level)
3660 raise_level = TRUE; /* advance to next level */
3662 if ((hi_pos = NewHiScore_MM()) >= 0)
3664 game_status = HALLOFFAME;
3666 // DrawHallOfFame(hi_pos);
3673 game_status = MAINMENU;
3689 // LoadScore(level_nr);
3691 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
3692 game_mm.score < highscore[MAX_SCORE_ENTRIES - 1].Score)
3695 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3697 if (game_mm.score > highscore[k].Score)
3699 /* player has made it to the hall of fame */
3701 if (k < MAX_SCORE_ENTRIES - 1)
3703 int m = MAX_SCORE_ENTRIES - 1;
3706 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3707 if (!strcmp(setup.player_name, highscore[l].Name))
3709 if (m == k) /* player's new highscore overwrites his old one */
3713 for (l = m; l>k; l--)
3715 strcpy(highscore[l].Name, highscore[l - 1].Name);
3716 highscore[l].Score = highscore[l - 1].Score;
3723 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3724 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3725 highscore[k].Score = game_mm.score;
3732 else if (!strncmp(setup.player_name, highscore[k].Name,
3733 MAX_PLAYER_NAME_LEN))
3734 break; /* player already there with a higher score */
3739 // if (position >= 0)
3740 // SaveScore(level_nr);
3745 static void InitMovingField_MM(int x, int y, int direction)
3747 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3748 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3750 MovDir[x][y] = direction;
3751 MovDir[newx][newy] = direction;
3753 if (Feld[newx][newy] == EL_EMPTY)
3754 Feld[newx][newy] = EL_BLOCKED;
3757 static void Moving2Blocked_MM(int x, int y, int *goes_to_x, int *goes_to_y)
3759 int direction = MovDir[x][y];
3760 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3761 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3767 static void Blocked2Moving_MM(int x, int y,
3768 int *comes_from_x, int *comes_from_y)
3770 int oldx = x, oldy = y;
3771 int direction = MovDir[x][y];
3773 if (direction == MV_LEFT)
3775 else if (direction == MV_RIGHT)
3777 else if (direction == MV_UP)
3779 else if (direction == MV_DOWN)
3782 *comes_from_x = oldx;
3783 *comes_from_y = oldy;
3786 static int MovingOrBlocked2Element_MM(int x, int y)
3788 int element = Feld[x][y];
3790 if (element == EL_BLOCKED)
3794 Blocked2Moving_MM(x, y, &oldx, &oldy);
3796 return Feld[oldx][oldy];
3803 static void RemoveField(int x, int y)
3805 Feld[x][y] = EL_EMPTY;
3812 static void RemoveMovingField_MM(int x, int y)
3814 int oldx = x, oldy = y, newx = x, newy = y;
3816 if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
3819 if (IS_MOVING(x, y))
3821 Moving2Blocked_MM(x, y, &newx, &newy);
3822 if (Feld[newx][newy] != EL_BLOCKED)
3825 else if (Feld[x][y] == EL_BLOCKED)
3827 Blocked2Moving_MM(x, y, &oldx, &oldy);
3828 if (!IS_MOVING(oldx, oldy))
3832 Feld[oldx][oldy] = EL_EMPTY;
3833 Feld[newx][newy] = EL_EMPTY;
3834 MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
3835 MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
3837 DrawLevelField_MM(oldx, oldy);
3838 DrawLevelField_MM(newx, newy);
3841 void PlaySoundLevel(int x, int y, int sound_nr)
3843 int sx = SCREENX(x), sy = SCREENY(y);
3845 int silence_distance = 8;
3847 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
3848 (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
3851 if (!IN_LEV_FIELD(x, y) ||
3852 sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
3853 sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
3856 volume = SOUND_MAX_VOLUME;
3859 stereo = (sx - SCR_FIELDX/2) * 12;
3861 stereo = SOUND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
3862 if (stereo > SOUND_MAX_RIGHT)
3863 stereo = SOUND_MAX_RIGHT;
3864 if (stereo < SOUND_MAX_LEFT)
3865 stereo = SOUND_MAX_LEFT;
3868 if (!IN_SCR_FIELD(sx, sy))
3870 int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
3871 int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
3873 volume -= volume * (dx > dy ? dx : dy) / silence_distance;
3876 PlaySoundExt(sound_nr, volume, stereo, SND_CTRL_PLAY_SOUND);
3879 static void RaiseScore_MM(int value)
3881 game_mm.score += value;
3884 DrawText(DX_SCORE, DY_SCORE, int2str(game_mm.score, 4),
3889 void RaiseScoreElement_MM(int element)
3894 RaiseScore_MM(native_mm_level.score[SC_PACMAN]);
3898 RaiseScore_MM(native_mm_level.score[SC_KEY]);