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 laser.overloaded = FALSE;
575 laser.stops_inside_element = FALSE;
577 DrawLaser(0, DL_LASER_ENABLED);
580 printf("Start scanning with LX == %d, LY == %d, XS == %d, YS == %d\n",
588 if (laser.num_edges > MAX_LASER_LEN || laser.num_damages > MAX_LASER_LEN)
591 laser.overloaded = TRUE;
596 hit_mask = ScanPixel();
599 printf("Hit something at LX == %d, LY == %d, XS == %d, YS == %d\n",
603 /* hit something -- check out what it was */
604 ELX = (LX + XS) / TILEX;
605 ELY = (LY + YS) / TILEY;
608 printf("hit_mask (1) == '%x' (%d, %d) (%d, %d)\n",
609 hit_mask, LX, LY, ELX, ELY);
612 if (!IN_LEV_FIELD(ELX, ELY) || !IN_PIX_FIELD(LX, LY))
615 laser.dest_element = element;
620 if (hit_mask == (HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT))
622 /* we have hit the top-right and bottom-left element --
623 choose the bottom-left one */
624 /* !!! THIS CAN BE DONE MORE INTELLIGENTLY, FOR EXAMPLE, IF ONE
625 ELEMENT WAS STEEL AND THE OTHER ONE WAS ICE => ALWAYS CHOOSE
626 THE ICE AND MELT IT AWAY INSTEAD OF OVERLOADING LASER !!! */
627 ELX = (LX - 2) / TILEX;
628 ELY = (LY + 2) / TILEY;
631 if (hit_mask == (HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT))
633 /* we have hit the top-left and bottom-right element --
634 choose the top-left one */
635 /* !!! SEE ABOVE !!! */
636 ELX = (LX - 2) / TILEX;
637 ELY = (LY - 2) / TILEY;
641 printf("hit_mask (2) == '%x' (%d, %d) (%d, %d)\n",
642 hit_mask, LX, LY, ELX, ELY);
645 element = Feld[ELX][ELY];
646 laser.dest_element = element;
649 printf("Hit element %d at (%d, %d) [%d, %d] [%d, %d] [%d]\n",
652 LX % TILEX, LY % TILEY,
657 if (!IN_LEV_FIELD(ELX, ELY))
658 printf("WARNING! (1) %d, %d (%d)\n", ELX, ELY, element);
661 if (element == EL_EMPTY)
663 if (!HitOnlyAnEdge(element, hit_mask))
666 else if (element == EL_FUSE_ON)
668 if (HitPolarizer(element, hit_mask))
671 else if (IS_GRID(element) || IS_DF_GRID(element))
673 if (HitPolarizer(element, hit_mask))
676 else if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD ||
677 element == EL_GATE_STONE || element == EL_GATE_WOOD)
679 if (HitBlock(element, hit_mask))
686 else if (IS_MCDUFFIN(element))
688 if (HitLaserSource(element, hit_mask))
691 else if ((element >= EL_EXIT_CLOSED && element <= EL_EXIT_OPEN) ||
692 IS_RECEIVER(element))
694 if (HitLaserDestination(element, hit_mask))
697 else if (IS_WALL(element))
699 if (IS_WALL_STEEL(element) || IS_DF_WALL_STEEL(element))
701 if (HitReflectingWalls(element, hit_mask))
706 if (HitAbsorbingWalls(element, hit_mask))
712 if (HitElement(element, hit_mask))
717 DrawLaser(rf - 1, DL_LASER_ENABLED);
718 rf = laser.num_edges;
722 if (laser.dest_element != Feld[ELX][ELY])
724 printf("ALARM: laser.dest_element == %d, Feld[ELX][ELY] == %d\n",
725 laser.dest_element, Feld[ELX][ELY]);
729 if (!end && !laser.stops_inside_element && !StepBehind())
732 printf("ScanLaser: Go one step back\n");
738 AddLaserEdge(LX, LY);
742 DrawLaser(rf - 1, DL_LASER_ENABLED);
747 if (!IN_LEV_FIELD(ELX, ELY))
748 printf("WARNING! (2) %d, %d\n", ELX, ELY);
752 void DrawLaserExt(int start_edge, int num_edges, int mode)
758 printf("DrawLaserExt: start_edge, num_edges, mode == %d, %d, %d\n",
759 start_edge, num_edges, mode);
764 Error(ERR_WARN, "DrawLaserExt: start_edge < 0");
771 Error(ERR_WARN, "DrawLaserExt: num_edges < 0");
777 if (mode == DL_LASER_DISABLED)
779 printf("DrawLaser: Delete laser from edge %d\n", start_edge);
783 /* now draw the laser to the backbuffer and (if enabled) to the screen */
784 DrawLines(drawto, &laser.edge[start_edge], num_edges,
785 (mode == DL_LASER_ENABLED ? pen_ray : pen_bg));
787 redraw_mask |= REDRAW_FIELD;
789 if (mode == DL_LASER_ENABLED)
792 /* after the laser was deleted, the "damaged" graphics must be restored */
793 if (laser.num_damages)
795 int damage_start = 0;
798 /* determine the starting edge, from which graphics need to be restored */
801 for (i = 0; i < laser.num_damages; i++)
803 if (laser.damage[i].edge == start_edge + 1)
812 /* restore graphics from this starting edge to the end of damage list */
813 for (i = damage_start; i < laser.num_damages; i++)
815 int lx = laser.damage[i].x;
816 int ly = laser.damage[i].y;
817 int element = Feld[lx][ly];
819 if (Hit[lx][ly] == laser.damage[i].edge)
820 if (!((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
823 if (Box[lx][ly] == laser.damage[i].edge)
826 if (IS_DRAWABLE(element))
827 DrawField_MM(lx, ly);
830 elx = laser.damage[damage_start].x;
831 ely = laser.damage[damage_start].y;
832 element = Feld[elx][ely];
835 if (IS_BEAMER(element))
839 for (i = 0; i < laser.num_beamers; i++)
840 printf("-> %d\n", laser.beamer_edge[i]);
841 printf("DrawLaserExt: IS_BEAMER: [%d]: Hit[%d][%d] == %d [%d]\n",
842 mode, elx, ely, Hit[elx][ely], start_edge);
843 printf("DrawLaserExt: IS_BEAMER: %d / %d\n",
844 get_element_angle(element), laser.damage[damage_start].angle);
848 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
849 laser.num_beamers > 0 &&
850 start_edge == laser.beamer_edge[laser.num_beamers - 1])
852 /* element is outgoing beamer */
853 laser.num_damages = damage_start + 1;
855 if (IS_BEAMER(element))
856 laser.current_angle = get_element_angle(element);
860 /* element is incoming beamer or other element */
861 laser.num_damages = damage_start;
862 laser.current_angle = laser.damage[laser.num_damages].angle;
867 /* no damages but McDuffin himself (who needs to be redrawn anyway) */
869 elx = laser.start_edge.x;
870 ely = laser.start_edge.y;
871 element = Feld[elx][ely];
874 laser.num_edges = start_edge + 1;
876 laser.current_angle = laser.start_angle;
878 LX = laser.edge[start_edge].x - (SX + 2);
879 LY = laser.edge[start_edge].y - (SY + 2);
880 XS = 2 * Step[laser.current_angle].x;
881 YS = 2 * Step[laser.current_angle].y;
884 printf("DrawLaser: Set (LX, LY) to (%d, %d) [%d]\n",
890 if (IS_BEAMER(element) ||
891 IS_FIBRE_OPTIC(element) ||
892 IS_PACMAN(element) ||
894 IS_POLAR_CROSS(element) ||
895 element == EL_FUSE_ON)
900 printf("element == %d\n", element);
903 if (IS_22_5_ANGLE(laser.current_angle)) /* neither 90° nor 45° angle */
904 step_size = ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) ? 4 : 3);
908 if (IS_POLAR(element) || IS_POLAR_CROSS(element) ||
909 ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
910 (laser.num_beamers == 0 ||
911 start_edge != laser.beamer_edge[laser.num_beamers - 1])))
913 /* element is incoming beamer or other element */
914 step_size = -step_size;
919 if (IS_BEAMER(element))
921 printf("start_edge == %d, laser.beamer_edge == %d\n",
922 start_edge, laser.beamer_edge);
926 LX += step_size * XS;
927 LY += step_size * YS;
929 else if (element != EL_EMPTY)
938 printf("DrawLaser: Finally: (LX, LY) to (%d, %d) [%d]\n",
943 void DrawLaser(int start_edge, int mode)
945 if (laser.num_edges - start_edge < 0)
947 Error(ERR_WARN, "DrawLaser: laser.num_edges - start_edge < 0");
952 /* check if laser is interrupted by beamer element */
953 if (laser.num_beamers > 0 &&
954 start_edge < laser.beamer_edge[laser.num_beamers - 1])
956 if (mode == DL_LASER_ENABLED)
959 int tmp_start_edge = start_edge;
961 /* draw laser segments forward from the start to the last beamer */
962 for (i = 0; i < laser.num_beamers; i++)
964 int tmp_num_edges = laser.beamer_edge[i] - tmp_start_edge;
966 if (tmp_num_edges <= 0)
970 printf("DrawLaser: DL_LASER_ENABLED: i==%d: %d, %d\n",
971 i, laser.beamer_edge[i], tmp_start_edge);
974 DrawLaserExt(tmp_start_edge, tmp_num_edges, DL_LASER_ENABLED);
976 tmp_start_edge = laser.beamer_edge[i];
979 /* draw last segment from last beamer to the end */
980 DrawLaserExt(tmp_start_edge, laser.num_edges - tmp_start_edge,
986 int last_num_edges = laser.num_edges;
987 int num_beamers = laser.num_beamers;
989 /* delete laser segments backward from the end to the first beamer */
990 for (i = num_beamers-1; i >= 0; i--)
992 int tmp_num_edges = last_num_edges - laser.beamer_edge[i];
994 if (laser.beamer_edge[i] - start_edge <= 0)
997 DrawLaserExt(laser.beamer_edge[i], tmp_num_edges, DL_LASER_DISABLED);
999 last_num_edges = laser.beamer_edge[i];
1000 laser.num_beamers--;
1004 if (last_num_edges - start_edge <= 0)
1005 printf("DrawLaser: DL_LASER_DISABLED: %d, %d\n",
1006 last_num_edges, start_edge);
1009 /* delete first segment from start to the first beamer */
1010 DrawLaserExt(start_edge, last_num_edges - start_edge, DL_LASER_DISABLED);
1015 DrawLaserExt(start_edge, laser.num_edges - start_edge, mode);
1019 boolean HitElement(int element, int hit_mask)
1021 if (HitOnlyAnEdge(element, hit_mask))
1024 if (IS_MOVING(ELX, ELY) || IS_BLOCKED(ELX, ELY))
1025 element = MovingOrBlocked2Element_MM(ELX, ELY);
1028 printf("HitElement (1): element == %d\n", element);
1032 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1033 printf("HitElement (%d): EXACT MATCH @ (%d, %d)\n", element, ELX, ELY);
1035 printf("HitElement (%d): FUZZY MATCH @ (%d, %d)\n", element, ELX, ELY);
1038 AddDamagedField(ELX, ELY);
1040 /* this is more precise: check if laser would go through the center */
1041 if ((ELX * TILEX + 14 - LX) * YS != (ELY * TILEY + 14 - LY) * XS)
1043 /* skip the whole element before continuing the scan */
1049 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1051 if (LX/TILEX > ELX || LY/TILEY > ELY)
1053 /* skipping scan positions to the right and down skips one scan
1054 position too much, because this is only the top left scan position
1055 of totally four scan positions (plus one to the right, one to the
1056 bottom and one to the bottom right) */
1066 printf("HitElement (2): element == %d\n", element);
1069 if (LX + 5 * XS < 0 ||
1079 printf("HitElement (3): element == %d\n", element);
1082 if (IS_POLAR(element) &&
1083 ((element - EL_POLAR_START) % 2 ||
1084 (element - EL_POLAR_START) / 2 != laser.current_angle % 8))
1086 PlaySoundStereo(SND_KINK, ST(ELX));
1088 laser.num_damages--;
1093 if (IS_POLAR_CROSS(element) &&
1094 (element - EL_POLAR_CROSS_START) != laser.current_angle % 4)
1096 PlaySoundStereo(SND_KINK, ST(ELX));
1098 laser.num_damages--;
1103 if (!IS_BEAMER(element) &&
1104 !IS_FIBRE_OPTIC(element) &&
1105 !IS_GRID_WOOD(element) &&
1106 element != EL_FUEL_EMPTY)
1109 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1110 printf("EXACT MATCH @ (%d, %d)\n", ELX, ELY);
1112 printf("FUZZY MATCH @ (%d, %d)\n", ELX, ELY);
1115 LX = ELX * TILEX + 14;
1116 LY = ELY * TILEY + 14;
1118 AddLaserEdge(LX, LY);
1121 if (IS_MIRROR(element) ||
1122 IS_MIRROR_FIXED(element) ||
1123 IS_POLAR(element) ||
1124 IS_POLAR_CROSS(element) ||
1125 IS_DF_MIRROR(element) ||
1126 IS_DF_MIRROR_AUTO(element) ||
1127 element == EL_PRISM ||
1128 element == EL_REFRACTOR)
1130 int current_angle = laser.current_angle;
1133 laser.num_damages--;
1135 AddDamagedField(ELX, ELY);
1137 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1140 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1142 if (IS_MIRROR(element) ||
1143 IS_MIRROR_FIXED(element) ||
1144 IS_DF_MIRROR(element) ||
1145 IS_DF_MIRROR_AUTO(element))
1146 laser.current_angle = get_mirrored_angle(laser.current_angle,
1147 get_element_angle(element));
1149 if (element == EL_PRISM || element == EL_REFRACTOR)
1150 laser.current_angle = RND(16);
1152 XS = 2 * Step[laser.current_angle].x;
1153 YS = 2 * Step[laser.current_angle].y;
1155 if (!IS_22_5_ANGLE(laser.current_angle)) /* 90° or 45° angle */
1160 LX += step_size * XS;
1161 LY += step_size * YS;
1164 /* draw sparkles on mirror */
1165 if ((IS_MIRROR(element) || IS_MIRROR_FIXED(element)) &&
1166 current_angle != laser.current_angle)
1168 MoveSprite(vp, &Pfeil[2], 4 + 16 * ELX, 5 + 16 * ELY + 1);
1172 if ((!IS_POLAR(element) && !IS_POLAR_CROSS(element)) &&
1173 current_angle != laser.current_angle)
1174 PlaySoundStereo(SND_LASER, ST(ELX));
1177 (get_opposite_angle(laser.current_angle) ==
1178 laser.damage[laser.num_damages - 1].angle ? TRUE : FALSE);
1180 return (laser.overloaded ? TRUE : FALSE);
1183 if (element == EL_FUEL_FULL)
1185 laser.stops_inside_element = TRUE;
1190 if (element == EL_BOMB || element == EL_MINE)
1192 PlaySoundStereo(SND_KINK, ST(ELX));
1194 if (element == EL_MINE)
1195 laser.overloaded = TRUE;
1198 if (element == EL_KETTLE ||
1199 element == EL_CELL ||
1200 element == EL_KEY ||
1201 element == EL_LIGHTBALL ||
1202 element == EL_PACMAN ||
1205 if (!IS_PACMAN(element))
1208 if (element == EL_PACMAN)
1211 if (element == EL_KETTLE || element == EL_CELL)
1213 if (game_mm.kettles_still_needed > 0)
1214 game_mm.kettles_still_needed--;
1218 if (game_mm.kettles_still_needed == 0)
1221 static int xy[4][2] =
1229 PlaySoundStereo(SND_KLING, ST(ELX));
1231 for (y = 0; y < lev_fieldy; y++)
1233 for (x = 0; x < lev_fieldx; x++)
1235 /* initiate opening animation of exit door */
1236 if (Feld[x][y] == EL_EXIT_CLOSED)
1237 Feld[x][y] = EL_EXIT_OPENING;
1239 /* remove field that blocks receiver */
1240 if (IS_RECEIVER(Feld[x][y]))
1242 int phase = Feld[x][y] - EL_RECEIVER_START;
1243 int blocking_x, blocking_y;
1245 blocking_x = x + xy[phase][0];
1246 blocking_y = y + xy[phase][1];
1248 if (IN_LEV_FIELD(blocking_x, blocking_y))
1250 Feld[blocking_x][blocking_y] = EL_EMPTY;
1252 DrawField_MM(blocking_x, blocking_y);
1258 DrawLaser(0, DL_LASER_ENABLED);
1261 else if (element == EL_KEY)
1265 else if (element == EL_LIGHTBALL)
1269 else if (IS_PACMAN(element))
1271 DeletePacMan(ELX, ELY);
1278 if (element == EL_LIGHTBULB_OFF || element == EL_LIGHTBULB_ON)
1280 PlaySoundStereo(SND_KINK, ST(ELX));
1282 DrawLaser(0, DL_LASER_ENABLED);
1284 if (Feld[ELX][ELY] == EL_LIGHTBULB_OFF)
1286 Feld[ELX][ELY] = EL_LIGHTBULB_ON;
1287 game_mm.lights_still_needed--;
1291 Feld[ELX][ELY] = EL_LIGHTBULB_OFF;
1292 game_mm.lights_still_needed++;
1295 DrawField_MM(ELX, ELY);
1296 DrawLaser(0, DL_LASER_ENABLED);
1301 laser.stops_inside_element = TRUE;
1307 printf("HitElement (4): element == %d\n", element);
1310 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1311 laser.num_beamers < MAX_NUM_BEAMERS &&
1312 laser.beamer[BEAMER_NR(element)][1].num)
1314 int beamer_angle = get_element_angle(element);
1315 int beamer_nr = BEAMER_NR(element);
1319 printf("HitElement (BEAMER): element == %d\n", element);
1322 laser.num_damages--;
1324 if (IS_FIBRE_OPTIC(element) ||
1325 laser.current_angle == get_opposite_angle(beamer_angle))
1329 LX = ELX * TILEX + 14;
1330 LY = ELY * TILEY + 14;
1332 AddLaserEdge(LX, LY);
1333 AddDamagedField(ELX, ELY);
1335 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1338 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1340 pos = (ELX == laser.beamer[beamer_nr][0].x &&
1341 ELY == laser.beamer[beamer_nr][0].y ? 1 : 0);
1342 ELX = laser.beamer[beamer_nr][pos].x;
1343 ELY = laser.beamer[beamer_nr][pos].y;
1344 LX = ELX * TILEX + 14;
1345 LY = ELY * TILEY + 14;
1347 if (IS_BEAMER(element))
1349 laser.current_angle = get_element_angle(Feld[ELX][ELY]);
1350 XS = 2 * Step[laser.current_angle].x;
1351 YS = 2 * Step[laser.current_angle].y;
1354 laser.beamer_edge[laser.num_beamers] = laser.num_edges;
1356 AddLaserEdge(LX, LY);
1357 AddDamagedField(ELX, ELY);
1359 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1362 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1364 if (laser.current_angle == (laser.current_angle >> 1) << 1)
1369 LX += step_size * XS;
1370 LY += step_size * YS;
1372 laser.num_beamers++;
1381 boolean HitOnlyAnEdge(int element, int hit_mask)
1383 /* check if the laser hit only the edge of an element and, if so, go on */
1386 printf("LX, LY, hit_mask == %d, %d, %d\n", LX, LY, hit_mask);
1389 if ((hit_mask == HIT_MASK_TOPLEFT ||
1390 hit_mask == HIT_MASK_TOPRIGHT ||
1391 hit_mask == HIT_MASK_BOTTOMLEFT ||
1392 hit_mask == HIT_MASK_BOTTOMRIGHT) &&
1393 laser.current_angle % 4) /* angle is not 90° */
1397 if (hit_mask == HIT_MASK_TOPLEFT)
1402 else if (hit_mask == HIT_MASK_TOPRIGHT)
1407 else if (hit_mask == HIT_MASK_BOTTOMLEFT)
1412 else /* (hit_mask == HIT_MASK_BOTTOMRIGHT) */
1418 AddDamagedField((LX + 2 * dx) / TILEX, (LY + 2 * dy) / TILEY);
1424 printf("[HitOnlyAnEdge() == TRUE]\n");
1431 printf("[HitOnlyAnEdge() == FALSE]\n");
1437 boolean HitPolarizer(int element, int hit_mask)
1439 if (HitOnlyAnEdge(element, hit_mask))
1442 if (IS_DF_GRID(element))
1444 int grid_angle = get_element_angle(element);
1447 printf("HitPolarizer: angle: grid == %d, laser == %d\n",
1448 grid_angle, laser.current_angle);
1451 AddLaserEdge(LX, LY);
1452 AddDamagedField(ELX, ELY);
1455 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1457 if (laser.current_angle == grid_angle ||
1458 laser.current_angle == get_opposite_angle(grid_angle))
1460 /* skip the whole element before continuing the scan */
1466 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1468 if (LX/TILEX > ELX || LY/TILEY > ELY)
1470 /* skipping scan positions to the right and down skips one scan
1471 position too much, because this is only the top left scan position
1472 of totally four scan positions (plus one to the right, one to the
1473 bottom and one to the bottom right) */
1479 AddLaserEdge(LX, LY);
1485 printf("HitPolarizer: LX, LY == %d, %d [%d, %d] [%d, %d]\n",
1487 LX / TILEX, LY / TILEY,
1488 LX % TILEX, LY % TILEY);
1493 else if (IS_GRID_STEEL_FIXED(element) || IS_GRID_STEEL_AUTO(element))
1495 return HitReflectingWalls(element, hit_mask);
1499 return HitAbsorbingWalls(element, hit_mask);
1502 else if (IS_GRID_STEEL(element))
1504 return HitReflectingWalls(element, hit_mask);
1506 else /* IS_GRID_WOOD */
1508 return HitAbsorbingWalls(element, hit_mask);
1514 boolean HitBlock(int element, int hit_mask)
1516 boolean check = FALSE;
1518 if ((element == EL_GATE_STONE || element == EL_GATE_WOOD) &&
1519 game_mm.num_keys == 0)
1522 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1525 int ex = ELX * TILEX + 14;
1526 int ey = ELY * TILEY + 14;
1530 for (i = 1; i < 32; i++)
1535 if ((x == ex || x == ex + 1) && (y == ey || y == ey + 1))
1540 if (check && (element == EL_BLOCK_WOOD || element == EL_GATE_WOOD))
1541 return HitAbsorbingWalls(element, hit_mask);
1545 AddLaserEdge(LX - XS, LY - YS);
1546 AddDamagedField(ELX, ELY);
1549 Box[ELX][ELY] = laser.num_edges;
1551 return HitReflectingWalls(element, hit_mask);
1554 if (element == EL_GATE_STONE || element == EL_GATE_WOOD)
1556 int xs = XS / 2, ys = YS / 2;
1557 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1558 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1560 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1561 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1563 laser.overloaded = (element == EL_GATE_STONE);
1568 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1569 (hit_mask == HIT_MASK_TOP ||
1570 hit_mask == HIT_MASK_LEFT ||
1571 hit_mask == HIT_MASK_RIGHT ||
1572 hit_mask == HIT_MASK_BOTTOM))
1573 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1574 hit_mask == HIT_MASK_BOTTOM),
1575 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1576 hit_mask == HIT_MASK_RIGHT));
1577 AddLaserEdge(LX, LY);
1583 if (element == EL_GATE_STONE && Box[ELX][ELY])
1585 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
1597 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1599 int xs = XS / 2, ys = YS / 2;
1600 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1601 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1603 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1604 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1606 laser.overloaded = (element == EL_BLOCK_STONE);
1611 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1612 (hit_mask == HIT_MASK_TOP ||
1613 hit_mask == HIT_MASK_LEFT ||
1614 hit_mask == HIT_MASK_RIGHT ||
1615 hit_mask == HIT_MASK_BOTTOM))
1616 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1617 hit_mask == HIT_MASK_BOTTOM),
1618 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1619 hit_mask == HIT_MASK_RIGHT));
1620 AddDamagedField(ELX, ELY);
1622 LX = ELX * TILEX + 14;
1623 LY = ELY * TILEY + 14;
1625 AddLaserEdge(LX, LY);
1627 laser.stops_inside_element = TRUE;
1635 boolean HitLaserSource(int element, int hit_mask)
1637 if (HitOnlyAnEdge(element, hit_mask))
1640 PlaySoundStereo(SND_AUTSCH, ST(ELX));
1641 laser.overloaded = TRUE;
1646 boolean HitLaserDestination(int element, int hit_mask)
1648 if (HitOnlyAnEdge(element, hit_mask))
1651 if (element != EL_EXIT_OPEN &&
1652 !(IS_RECEIVER(element) &&
1653 game_mm.kettles_still_needed == 0 &&
1654 laser.current_angle == get_opposite_angle(get_element_angle(element))))
1656 PlaySoundStereo(SND_HOLZ, ST(ELX));
1660 if (IS_RECEIVER(element) ||
1661 (IS_22_5_ANGLE(laser.current_angle) &&
1662 (ELX != (LX + 6 * XS) / TILEX ||
1663 ELY != (LY + 6 * YS) / TILEY ||
1672 LX = ELX * TILEX + 14;
1673 LY = ELY * TILEY + 14;
1675 laser.stops_inside_element = TRUE;
1678 AddLaserEdge(LX, LY);
1679 AddDamagedField(ELX, ELY);
1681 if (game_mm.lights_still_needed == 0)
1682 game_mm.level_solved = TRUE;
1687 boolean HitReflectingWalls(int element, int hit_mask)
1689 /* check if laser hits side of a wall with an angle that is not 90° */
1690 if (!IS_90_ANGLE(laser.current_angle) && (hit_mask == HIT_MASK_TOP ||
1691 hit_mask == HIT_MASK_LEFT ||
1692 hit_mask == HIT_MASK_RIGHT ||
1693 hit_mask == HIT_MASK_BOTTOM))
1695 PlaySoundStereo(SND_HUI, ST(ELX));
1700 if (!IS_DF_GRID(element))
1701 AddLaserEdge(LX, LY);
1703 /* check if laser hits wall with an angle of 45° */
1704 if (!IS_22_5_ANGLE(laser.current_angle))
1706 if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1709 laser.current_angle = get_mirrored_angle(laser.current_angle,
1712 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1715 laser.current_angle = get_mirrored_angle(laser.current_angle,
1719 AddLaserEdge(LX, LY);
1721 XS = 2 * Step[laser.current_angle].x;
1722 YS = 2 * Step[laser.current_angle].y;
1726 else if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1728 laser.current_angle = get_mirrored_angle(laser.current_angle,
1733 if (!IS_DF_GRID(element))
1734 AddLaserEdge(LX, LY);
1739 if (!IS_DF_GRID(element))
1740 AddLaserEdge(LX, LY + YS / 2);
1743 if (!IS_DF_GRID(element))
1744 AddLaserEdge(LX, LY);
1747 YS = 2 * Step[laser.current_angle].y;
1751 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1753 laser.current_angle = get_mirrored_angle(laser.current_angle,
1758 if (!IS_DF_GRID(element))
1759 AddLaserEdge(LX, LY);
1764 if (!IS_DF_GRID(element))
1765 AddLaserEdge(LX + XS / 2, LY);
1768 if (!IS_DF_GRID(element))
1769 AddLaserEdge(LX, LY);
1772 XS = 2 * Step[laser.current_angle].x;
1778 /* reflection at the edge of reflecting DF style wall */
1779 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1781 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1782 hit_mask == HIT_MASK_TOPRIGHT) ||
1783 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1784 hit_mask == HIT_MASK_TOPLEFT) ||
1785 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1786 hit_mask == HIT_MASK_BOTTOMLEFT) ||
1787 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1788 hit_mask == HIT_MASK_BOTTOMRIGHT))
1791 (hit_mask == HIT_MASK_TOPRIGHT || hit_mask == HIT_MASK_BOTTOMLEFT ?
1792 ANG_MIRROR_135 : ANG_MIRROR_45);
1794 PlaySoundStereo(SND_HUI, ST(ELX));
1796 AddDamagedField(ELX, ELY);
1797 AddLaserEdge(LX, LY);
1799 laser.current_angle = get_mirrored_angle(laser.current_angle,
1807 AddLaserEdge(LX, LY);
1813 /* reflection inside an edge of reflecting DF style wall */
1814 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1816 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1817 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT)) ||
1818 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1819 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMRIGHT)) ||
1820 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1821 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT)) ||
1822 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1823 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPLEFT)))
1826 (hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT) ||
1827 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT) ?
1828 ANG_MIRROR_135 : ANG_MIRROR_45);
1830 PlaySoundStereo(SND_HUI, ST(ELX));
1833 AddDamagedField(ELX, ELY);
1836 AddLaserEdge(LX - XS, LY - YS);
1837 AddLaserEdge(LX - XS + (ABS(XS) == 4 ? XS/2 : 0),
1838 LY - YS + (ABS(YS) == 4 ? YS/2 : 0));
1840 laser.current_angle = get_mirrored_angle(laser.current_angle,
1848 AddLaserEdge(LX, LY);
1854 /* check if laser hits DF style wall with an angle of 90° */
1855 if (IS_DF_WALL(element) && IS_90_ANGLE(laser.current_angle))
1857 if ((IS_HORIZ_ANGLE(laser.current_angle) &&
1858 (!(hit_mask & HIT_MASK_TOP) || !(hit_mask & HIT_MASK_BOTTOM))) ||
1859 (IS_VERT_ANGLE(laser.current_angle) &&
1860 (!(hit_mask & HIT_MASK_LEFT) || !(hit_mask & HIT_MASK_RIGHT))))
1862 static int last_LX = 0, last_LY = 0, last_hit_mask = 0;
1864 /* laser at last step touched nothing or the same side of the wall */
1865 if (LX != last_LX || LY != last_LY || hit_mask == last_hit_mask)
1867 AddDamagedField(ELX, ELY);
1874 last_hit_mask = hit_mask;
1881 if (!HitOnlyAnEdge(element, hit_mask))
1883 laser.overloaded = TRUE;
1891 boolean HitAbsorbingWalls(int element, int hit_mask)
1893 if (HitOnlyAnEdge(element, hit_mask))
1897 (hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT))
1899 AddLaserEdge(LX - XS, LY - YS);
1906 (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM))
1908 AddLaserEdge(LX - XS, LY - YS);
1914 if (IS_WALL_WOOD(element) ||
1915 IS_DF_WALL_WOOD(element) ||
1916 IS_GRID_WOOD(element) ||
1917 IS_GRID_WOOD_FIXED(element) ||
1918 IS_GRID_WOOD_AUTO(element) ||
1919 element == EL_FUSE_ON ||
1920 element == EL_BLOCK_WOOD ||
1921 element == EL_GATE_WOOD)
1923 PlaySoundStereo(SND_HOLZ, ST(ELX));
1928 if (IS_WALL_ICE(element))
1932 mask = (LX + XS) / MINI_TILEX - ELX * 2 + 1; /* Quadrant (horizontal) */
1933 mask <<= (((LY + YS) / MINI_TILEY - ELY * 2) > 0) * 2; /* || (vertical) */
1935 /* check if laser hits wall with an angle of 90° */
1936 if (IS_90_ANGLE(laser.current_angle))
1937 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1939 if (mask == 1 || mask == 2 || mask == 4 || mask == 8)
1943 for (i = 0; i < 4; i++)
1945 if (mask == (1 << i) && (XS > 0) == (i % 2) && (YS > 0) == (i / 2))
1946 mask = 15 - (8 >> i);
1947 else if (ABS(XS) == 4 &&
1949 (XS > 0) == (i % 2) &&
1950 (YS < 0) == (i / 2))
1951 mask = 3 + (i / 2) * 9;
1952 else if (ABS(YS) == 4 &&
1954 (XS < 0) == (i % 2) &&
1955 (YS > 0) == (i / 2))
1956 mask = 5 + (i % 2) * 5;
1960 laser.wall_mask = mask;
1962 else if (IS_WALL_AMOEBA(element))
1964 int elx = (LX - 2 * XS) / TILEX;
1965 int ely = (LY - 2 * YS) / TILEY;
1966 int element2 = Feld[elx][ely];
1969 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element2))
1971 laser.dest_element = EL_EMPTY;
1979 mask = (LX - 2 * XS) / 16 - ELX * 2 + 1;
1980 mask <<= ((LY - 2 * YS) / 16 - ELY * 2) * 2;
1982 if (IS_90_ANGLE(laser.current_angle))
1983 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1985 laser.dest_element = element2 | EL_WALL_AMOEBA;
1987 laser.wall_mask = mask;
1993 void OpenExit(int x, int y)
1997 if (!MovDelay[x][y]) /* next animation frame */
1998 MovDelay[x][y] = 4 * delay;
2000 if (MovDelay[x][y]) /* wait some time before next frame */
2005 phase = MovDelay[x][y] / delay;
2007 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2008 DrawGraphicAnimation_MM(x, y, IMG_MM_EXIT_OPENING, 3 - phase);
2010 if (!MovDelay[x][y])
2012 Feld[x][y] = EL_EXIT_OPEN;
2018 void OpenSurpriseBall(int x, int y)
2022 if (!MovDelay[x][y]) /* next animation frame */
2023 MovDelay[x][y] = 50 * delay;
2025 if (MovDelay[x][y]) /* wait some time before next frame */
2029 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2032 int graphic = el2gfx(Store[x][y]);
2034 int dx = RND(26), dy = RND(26);
2036 getGraphicSource(graphic, 0, &bitmap, &gx, &gy);
2038 BlitBitmap(bitmap, drawto, gx + dx, gy + dy, 6, 6,
2039 SX + x * TILEX + dx, SY + y * TILEY + dy);
2041 MarkTileDirty(x, y);
2044 if (!MovDelay[x][y])
2046 Feld[x][y] = Store[x][y];
2055 void MeltIce(int x, int y)
2060 if (!MovDelay[x][y]) /* next animation frame */
2061 MovDelay[x][y] = frames * delay;
2063 if (MovDelay[x][y]) /* wait some time before next frame */
2066 int wall_mask = Store2[x][y];
2067 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_ICE;
2070 phase = frames - MovDelay[x][y] / delay - 1;
2072 if (!MovDelay[x][y])
2076 Feld[x][y] = real_element & (wall_mask ^ 0xFF);
2077 Store[x][y] = Store2[x][y] = 0;
2079 DrawWalls_MM(x, y, Feld[x][y]);
2081 if (Feld[x][y] == EL_WALL_ICE)
2082 Feld[x][y] = EL_EMPTY;
2084 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
2085 if (laser.damage[i].is_mirror)
2089 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
2091 DrawLaser(0, DL_LASER_DISABLED);
2095 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2097 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2099 laser.redraw = TRUE;
2104 void GrowAmoeba(int x, int y)
2109 if (!MovDelay[x][y]) /* next animation frame */
2110 MovDelay[x][y] = frames * delay;
2112 if (MovDelay[x][y]) /* wait some time before next frame */
2115 int wall_mask = Store2[x][y];
2116 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_AMOEBA;
2119 phase = MovDelay[x][y] / delay;
2121 if (!MovDelay[x][y])
2123 Feld[x][y] = real_element;
2124 Store[x][y] = Store2[x][y] = 0;
2126 DrawWalls_MM(x, y, Feld[x][y]);
2127 DrawLaser(0, DL_LASER_ENABLED);
2129 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2131 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2136 static void Explode_MM(int x, int y, int phase, int mode)
2138 int num_phase = 9, delay = 2;
2139 int last_phase = num_phase * delay;
2140 int half_phase = (num_phase / 2) * delay;
2142 laser.redraw = TRUE;
2144 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2146 int center_element = Feld[x][y];
2148 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2150 /* put moving element to center field (and let it explode there) */
2151 center_element = MovingOrBlocked2Element_MM(x, y);
2152 RemoveMovingField_MM(x, y);
2154 Feld[x][y] = center_element;
2157 if (center_element == EL_BOMB || IS_MCDUFFIN(center_element))
2158 Store[x][y] = center_element;
2160 Store[x][y] = EL_EMPTY;
2162 Store2[x][y] = mode;
2163 Feld[x][y] = EL_EXPLODING_OPAQUE;
2164 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2170 Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
2172 if (phase == half_phase)
2174 Feld[x][y] = EL_EXPLODING_TRANSP;
2176 if (x == ELX && y == ELY)
2180 if (phase == last_phase)
2182 if (Store[x][y] == EL_BOMB)
2184 laser.num_damages--;
2185 DrawLaser(0, DL_LASER_DISABLED);
2186 laser.num_edges = 0;
2188 Bang_MM(laser.start_edge.x, laser.start_edge.y);
2189 Store[x][y] = EL_EMPTY;
2191 else if (IS_MCDUFFIN(Store[x][y]))
2193 game_mm.game_over = TRUE;
2194 game_mm.game_over_cause = GAME_OVER_BOMB;
2195 Store[x][y] = EL_EMPTY;
2198 Feld[x][y] = Store[x][y];
2199 Store[x][y] = Store2[x][y] = 0;
2200 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2202 InitField(x, y, FALSE);
2205 else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2207 int graphic = IMG_MM_DEFAULT_EXPLODING;
2208 int graphic_phase = (phase / delay - 1);
2212 if (Store2[x][y] == EX_KETTLE)
2214 if (graphic_phase < 3)
2216 graphic = IMG_MM_KETTLE_EXPLODING;
2218 else if (graphic_phase < 5)
2224 graphic = IMG_EMPTY;
2228 else if (Store2[x][y] == EX_SHORT)
2230 if (graphic_phase < 4)
2236 graphic = IMG_EMPTY;
2241 getGraphicSource(graphic, graphic_phase, &bitmap, &src_x, &src_y);
2243 BlitBitmap(bitmap, drawto_field, src_x, src_y, TILEX, TILEY,
2244 FX + x * TILEX, FY + y * TILEY);
2246 MarkTileDirty(x, y);
2250 static void Bang_MM(int x, int y)
2252 int element = Feld[x][y];
2253 int mode = EX_NORMAL;
2256 DrawLaser(0, DL_LASER_ENABLED);
2275 if (IS_PACMAN(element))
2276 PlaySoundStereo(SND_QUIEK, ST(x));
2277 else if (element == EL_BOMB || IS_MCDUFFIN(element))
2278 PlaySoundStereo(SND_ROAAAR, ST(x));
2279 else if (element == EL_KEY)
2280 PlaySoundStereo(SND_KLING, ST(x));
2282 PlaySoundStereo((mode == EX_SHORT ? SND_WHOOSH : SND_KABUMM), ST(x));
2284 Explode_MM(x, y, EX_PHASE_START, mode);
2287 void TurnRound(int x, int y)
2299 { 0, 0 }, { 0, 0 }, { 0, 0 },
2304 int left, right, back;
2308 { MV_DOWN, MV_UP, MV_RIGHT },
2309 { MV_UP, MV_DOWN, MV_LEFT },
2311 { MV_LEFT, MV_RIGHT, MV_DOWN },
2312 { 0,0,0 }, { 0,0,0 }, { 0,0,0 },
2313 { MV_RIGHT, MV_LEFT, MV_UP }
2316 int element = Feld[x][y];
2317 int old_move_dir = MovDir[x][y];
2318 int right_dir = turn[old_move_dir].right;
2319 int back_dir = turn[old_move_dir].back;
2320 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
2321 int right_x = x + right_dx, right_y = y + right_dy;
2323 if (element == EL_PACMAN)
2325 boolean can_turn_right = FALSE;
2327 if (IN_LEV_FIELD(right_x, right_y) &&
2328 IS_EATABLE4PACMAN(Feld[right_x][right_y]))
2329 can_turn_right = TRUE;
2332 MovDir[x][y] = right_dir;
2334 MovDir[x][y] = back_dir;
2340 static void StartMoving_MM(int x, int y)
2342 int element = Feld[x][y];
2347 if (CAN_MOVE(element))
2351 if (MovDelay[x][y]) /* wait some time before next movement */
2359 /* now make next step */
2361 Moving2Blocked_MM(x, y, &newx, &newy); /* get next screen position */
2363 if (element == EL_PACMAN &&
2364 IN_LEV_FIELD(newx, newy) && IS_EATABLE4PACMAN(Feld[newx][newy]) &&
2365 !ObjHit(newx, newy, HIT_POS_CENTER))
2367 Store[newx][newy] = Feld[newx][newy];
2368 Feld[newx][newy] = EL_EMPTY;
2370 DrawField_MM(newx, newy);
2372 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy) ||
2373 ObjHit(newx, newy, HIT_POS_CENTER))
2375 /* object was running against a wall */
2382 InitMovingField_MM(x, y, MovDir[x][y]);
2386 ContinueMoving_MM(x, y);
2389 static void ContinueMoving_MM(int x, int y)
2391 int element = Feld[x][y];
2392 int direction = MovDir[x][y];
2393 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2394 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2395 int horiz_move = (dx!=0);
2396 int newx = x + dx, newy = y + dy;
2397 int step = (horiz_move ? dx : dy) * TILEX / 8;
2399 MovPos[x][y] += step;
2401 if (ABS(MovPos[x][y]) >= TILEX) /* object reached its destination */
2403 Feld[x][y] = EL_EMPTY;
2404 Feld[newx][newy] = element;
2406 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2407 MovDelay[newx][newy] = 0;
2409 if (!CAN_MOVE(element))
2410 MovDir[newx][newy] = 0;
2413 DrawField_MM(newx, newy);
2415 Stop[newx][newy] = TRUE;
2417 if (element == EL_PACMAN)
2419 if (Store[newx][newy] == EL_BOMB)
2420 Bang_MM(newx, newy);
2422 if (IS_WALL_AMOEBA(Store[newx][newy]) &&
2423 (LX + 2 * XS) / TILEX == newx &&
2424 (LY + 2 * YS) / TILEY == newy)
2431 else /* still moving on */
2436 laser.redraw = TRUE;
2439 void ClickElement(int mx, int my, int button)
2441 static unsigned int click_delay = 0;
2442 static int click_delay_value = CLICK_DELAY_SHORT;
2443 static boolean new_button = TRUE;
2445 int x = (mx - SX) / TILEX, y = (my - SY) / TILEY;
2447 /* do not rotate objects hit by the laser after the game was solved */
2448 if (game_mm.level_solved && Hit[x][y])
2451 if (button == MB_RELEASED)
2454 click_delay_value = CLICK_DELAY_SHORT;
2456 /* release eventually hold auto-rotating mirror */
2457 RotateMirror(x, y, MB_RELEASED);
2462 if (!DelayReached(&click_delay, click_delay_value) && !new_button)
2465 if (button == MB_MIDDLEBUTTON) /* middle button has no function */
2468 if (!IN_PIX_FIELD(mx - SX, my - SY))
2471 if (Feld[x][y] == EL_EMPTY)
2474 element = Feld[x][y];
2476 if (IS_MIRROR(element) ||
2477 IS_BEAMER(element) ||
2478 IS_POLAR(element) ||
2479 IS_POLAR_CROSS(element) ||
2480 IS_DF_MIRROR(element) ||
2481 IS_DF_MIRROR_AUTO(element))
2483 RotateMirror(x, y, button);
2485 else if (IS_MCDUFFIN(element))
2487 if (!laser.fuse_off)
2489 DrawLaser(0, DL_LASER_DISABLED);
2496 element = get_rotated_element(element, BUTTON_ROTATION(button));
2497 laser.start_angle = get_element_angle(element);
2501 Feld[x][y] = element;
2508 if (!laser.fuse_off)
2511 else if (element == EL_FUSE_ON && laser.fuse_off)
2513 if (x != laser.fuse_x || y != laser.fuse_y)
2516 laser.fuse_off = FALSE;
2517 laser.fuse_x = laser.fuse_y = -1;
2519 DrawGraphic_MM(x, y, IMG_MM_FUSE_ACTIVE);
2522 else if (element == EL_FUSE_ON && !laser.fuse_off && new_button)
2524 laser.fuse_off = TRUE;
2527 laser.overloaded = FALSE;
2529 DrawLaser(0, DL_LASER_DISABLED);
2530 DrawGraphic_MM(x, y, IMG_MM_FUSE);
2532 else if (element == EL_LIGHTBALL)
2536 DrawLaser(0, DL_LASER_ENABLED);
2539 click_delay_value = (new_button ? CLICK_DELAY_LONG : CLICK_DELAY_SHORT);
2543 void RotateMirror(int x, int y, int button)
2545 static int hold_x = -1, hold_y = -1;
2547 if (button == MB_RELEASED)
2549 /* release eventually hold auto-rotating mirror */
2556 if (IS_MIRROR(Feld[x][y]) ||
2557 IS_POLAR_CROSS(Feld[x][y]) ||
2558 IS_POLAR(Feld[x][y]) ||
2559 IS_BEAMER(Feld[x][y]) ||
2560 IS_DF_MIRROR(Feld[x][y]) ||
2561 IS_GRID_STEEL_AUTO(Feld[x][y]) ||
2562 IS_GRID_WOOD_AUTO(Feld[x][y]))
2564 Feld[x][y] = get_rotated_element(Feld[x][y], BUTTON_ROTATION(button));
2566 else if (IS_DF_MIRROR_AUTO(Feld[x][y]))
2568 if (button == MB_LEFTBUTTON)
2570 /* left mouse button only for manual adjustment, no auto-rotating;
2571 freeze mirror for until mouse button released */
2575 else if (button == MB_RIGHTBUTTON && (hold_x != x || hold_y != y))
2577 Feld[x][y] = get_rotated_element(Feld[x][y], ROTATE_RIGHT);
2581 if (IS_GRID_STEEL_AUTO(Feld[x][y]) || IS_GRID_WOOD_AUTO(Feld[x][y]))
2583 int edge = Hit[x][y];
2589 DrawLaser(edge - 1, DL_LASER_DISABLED);
2593 else if (ObjHit(x, y, HIT_POS_CENTER))
2595 int edge = Hit[x][y];
2599 Error(ERR_WARN, "RotateMirror: inconsistent field Hit[][]!\n");
2603 DrawLaser(edge - 1, DL_LASER_DISABLED);
2610 if (ObjHit(x, y, HIT_POS_EDGE | HIT_POS_BETWEEN))
2615 if ((IS_BEAMER(Feld[x][y]) ||
2616 IS_POLAR(Feld[x][y]) ||
2617 IS_POLAR_CROSS(Feld[x][y])) && x == ELX && y == ELY)
2621 if (IS_BEAMER(Feld[x][y]))
2624 printf("TEST (%d, %d) [%d] [%d]\n",
2626 laser.beamer_edge, laser.beamer[1].num);
2636 DrawLaser(0, DL_LASER_ENABLED);
2640 void AutoRotateMirrors()
2642 static unsigned int rotate_delay = 0;
2645 if (!DelayReached(&rotate_delay, AUTO_ROTATE_DELAY))
2648 for (x = 0; x < lev_fieldx; x++)
2650 for (y = 0; y < lev_fieldy; y++)
2652 int element = Feld[x][y];
2654 /* do not rotate objects hit by the laser after the game was solved */
2655 if (game_mm.level_solved && Hit[x][y])
2658 if (IS_DF_MIRROR_AUTO(element) ||
2659 IS_GRID_WOOD_AUTO(element) ||
2660 IS_GRID_STEEL_AUTO(element) ||
2661 element == EL_REFRACTOR)
2662 RotateMirror(x, y, MB_RIGHTBUTTON);
2667 boolean ObjHit(int obx, int oby, int bits)
2674 if (bits & HIT_POS_CENTER)
2676 if (ReadPixel(drawto, SX + obx + 15, SY + oby + 15) == pen_ray)
2680 if (bits & HIT_POS_EDGE)
2682 for (i = 0; i < 4; i++)
2683 if (ReadPixel(drawto,
2684 SX + obx + 31 * (i % 2),
2685 SY + oby + 31 * (i / 2)) == pen_ray)
2689 if (bits & HIT_POS_BETWEEN)
2691 for (i = 0; i < 4; i++)
2692 if (ReadPixel(drawto,
2693 SX + 4 + obx + 22 * (i % 2),
2694 SY + 4 + oby + 22 * (i / 2)) == pen_ray)
2701 void DeletePacMan(int px, int py)
2707 if (game_mm.num_pacman <= 1)
2709 game_mm.num_pacman = 0;
2713 for (i = 0; i < game_mm.num_pacman; i++)
2714 if (game_mm.pacman[i].x == px && game_mm.pacman[i].y == py)
2717 game_mm.num_pacman--;
2719 for (j = i; j < game_mm.num_pacman; j++)
2721 game_mm.pacman[j].x = game_mm.pacman[j + 1].x;
2722 game_mm.pacman[j].y = game_mm.pacman[j + 1].y;
2723 game_mm.pacman[j].dir = game_mm.pacman[j + 1].dir;
2727 void ColorCycling(void)
2729 static int CC, Cc = 0;
2731 static int color, old = 0xF00, new = 0x010, mult = 1;
2732 static unsigned short red, green, blue;
2734 if (color_status == STATIC_COLORS)
2739 if (CC < Cc || CC > Cc + 50)
2743 color = old + new * mult;
2749 if (ABS(mult) == 16)
2759 red = 0x0e00 * ((color & 0xF00) >> 8);
2760 green = 0x0e00 * ((color & 0x0F0) >> 4);
2761 blue = 0x0e00 * ((color & 0x00F));
2762 SetRGB(pen_magicolor[0], red, green, blue);
2764 red = 0x1111 * ((color & 0xF00) >> 8);
2765 green = 0x1111 * ((color & 0x0F0) >> 4);
2766 blue = 0x1111 * ((color & 0x00F));
2767 SetRGB(pen_magicolor[1], red, green, blue);
2771 static void GameActions_MM_Ext(byte action[MAX_PLAYERS], boolean warp_mode)
2773 static unsigned int action_delay = 0;
2774 static unsigned int pacman_delay = 0;
2775 static unsigned int energy_delay = 0;
2776 static unsigned int overload_delay = 0;
2782 WaitUntilDelayReached(&action_delay, GameFrameDelay);
2784 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2787 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2789 element = Feld[x][y];
2791 if (!IS_MOVING(x, y) && CAN_MOVE(element))
2792 StartMoving_MM(x, y);
2793 else if (IS_MOVING(x, y))
2794 ContinueMoving_MM(x, y);
2795 else if (IS_EXPLODING(element))
2796 Explode_MM(x, y, Frame[x][y], EX_NORMAL);
2797 else if (element == EL_EXIT_OPENING)
2799 else if (element == EL_GRAY_BALL_OPENING)
2800 OpenSurpriseBall(x, y);
2801 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_ICE)
2803 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_AMOEBA)
2807 AutoRotateMirrors();
2810 /* !!! CHANGE THIS: REDRAW ONLY WHEN NEEDED !!! */
2812 /* redraw after Explode_MM() ... */
2814 DrawLaser(0, DL_LASER_ENABLED);
2815 laser.redraw = FALSE;
2820 if (game_mm.num_pacman && DelayReached(&pacman_delay, 250))
2824 if (laser.num_damages > MAX_LASER_LEN && !laser.fuse_off)
2826 DrawLaser(0, DL_LASER_DISABLED);
2831 if (DelayReached(&energy_delay, 4000))
2833 game_mm.energy_left--;
2834 if (game_mm.energy_left >= 0)
2837 BlitBitmap(pix[PIX_DOOR], drawto,
2838 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
2839 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
2840 DX_ENERGY, DY_ENERGY);
2842 redraw_mask |= REDRAW_DOOR_1;
2844 else if (setup.time_limit)
2848 for (i = 15; i >= 0; i--)
2851 SetRGB(pen_ray, 0x0000, 0x0000, i * color_scale);
2853 pen_ray = GetPixelFromRGB(window,
2854 native_mm_level.laser_red * 0x11 * i,
2855 native_mm_level.laser_green * 0x11 * i,
2856 native_mm_level.laser_blue * 0x11 * i);
2858 DrawLaser(0, DL_LASER_ENABLED);
2863 StopSound(SND_WARNTON);
2866 DrawLaser(0, DL_LASER_DISABLED);
2867 game_mm.game_over = TRUE;
2868 game_mm.game_over_cause = GAME_OVER_NO_ENERGY;
2871 if (Request("Out of magic energy ! Play it again ?",
2872 REQ_ASK | REQ_STAY_CLOSED))
2878 game_status = MAINMENU;
2887 element = laser.dest_element;
2890 if (element != Feld[ELX][ELY])
2892 printf("element == %d, Feld[ELX][ELY] == %d\n",
2893 element, Feld[ELX][ELY]);
2897 if (!laser.overloaded && laser.overload_value == 0 &&
2898 element != EL_BOMB &&
2899 element != EL_MINE &&
2900 element != EL_BALL_GRAY &&
2901 element != EL_BLOCK_STONE &&
2902 element != EL_BLOCK_WOOD &&
2903 element != EL_FUSE_ON &&
2904 element != EL_FUEL_FULL &&
2905 !IS_WALL_ICE(element) &&
2906 !IS_WALL_AMOEBA(element))
2909 if (((laser.overloaded && laser.overload_value < MAX_LASER_OVERLOAD) ||
2910 (!laser.overloaded && laser.overload_value > 0)) &&
2911 DelayReached(&overload_delay, 60 + !laser.overloaded * 120))
2913 if (laser.overloaded)
2914 laser.overload_value++;
2916 laser.overload_value--;
2918 if (game_mm.cheat_no_overload)
2920 laser.overloaded = FALSE;
2921 laser.overload_value = 0;
2924 if (laser.overload_value < MAX_LASER_OVERLOAD - 8)
2926 int color_up = 0xFF * laser.overload_value / MAX_LASER_OVERLOAD;
2927 int color_down = 0xFF - color_up;
2930 SetRGB(pen_ray, (laser.overload_value / 6) * color_scale, 0x0000,
2931 (15 - (laser.overload_value / 6)) * color_scale);
2934 GetPixelFromRGB(window,
2935 (native_mm_level.laser_red ? 0xFF : color_up),
2936 (native_mm_level.laser_green ? color_down : 0x00),
2937 (native_mm_level.laser_blue ? color_down : 0x00));
2939 DrawLaser(0, DL_LASER_ENABLED);
2943 if (laser.overloaded)
2945 if (setup.sound_loops)
2946 PlaySoundExt(SND_WARNTON, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
2947 SND_CTRL_PLAY_LOOP);
2949 PlaySoundStereo(SND_WARNTON, SOUND_MAX_RIGHT);
2952 if (!laser.overloaded)
2953 StopSound(SND_WARNTON);
2955 if (laser.overloaded)
2958 BlitBitmap(pix[PIX_DOOR], drawto,
2959 DOOR_GFX_PAGEX4 + XX_OVERLOAD,
2960 DOOR_GFX_PAGEY1 + YY_OVERLOAD + OVERLOAD_YSIZE
2961 - laser.overload_value,
2962 OVERLOAD_XSIZE, laser.overload_value,
2963 DX_OVERLOAD, DY_OVERLOAD + OVERLOAD_YSIZE
2964 - laser.overload_value);
2966 redraw_mask |= REDRAW_DOOR_1;
2971 BlitBitmap(pix[PIX_DOOR], drawto,
2972 DOOR_GFX_PAGEX5 + XX_OVERLOAD, DOOR_GFX_PAGEY1 + YY_OVERLOAD,
2973 OVERLOAD_XSIZE, OVERLOAD_YSIZE - laser.overload_value,
2974 DX_OVERLOAD, DY_OVERLOAD);
2976 redraw_mask |= REDRAW_DOOR_1;
2979 if (laser.overload_value == MAX_LASER_OVERLOAD)
2983 for (i = 15; i >= 0; i--)
2986 SetRGB(pen_ray, i * color_scale, 0x0000, 0x0000);
2989 pen_ray = GetPixelFromRGB(window, 0x11 * i, 0x00, 0x00);
2991 DrawLaser(0, DL_LASER_ENABLED);
2996 DrawLaser(0, DL_LASER_DISABLED);
2998 game_mm.game_over = TRUE;
2999 game_mm.game_over_cause = GAME_OVER_OVERLOADED;
3002 if (Request("Magic spell hit Mc Duffin ! Play it again ?",
3003 REQ_ASK | REQ_STAY_CLOSED))
3009 game_status = MAINMENU;
3023 if (element == EL_BOMB && CT > 1500)
3025 if (game_mm.cheat_no_explosion)
3029 laser.num_damages--;
3030 DrawLaser(0, DL_LASER_DISABLED);
3031 laser.num_edges = 0;
3036 laser.dest_element = EL_EXPLODING_OPAQUE;
3040 laser.num_damages--;
3041 DrawLaser(0, DL_LASER_DISABLED);
3043 laser.num_edges = 0;
3044 Bang_MM(laser.start_edge.x, laser.start_edge.y);
3046 if (Request("Bomb killed Mc Duffin ! Play it again ?",
3047 REQ_ASK | REQ_STAY_CLOSED))
3053 game_status = MAINMENU;
3061 if (element == EL_FUSE_ON && CT > 500)
3063 laser.fuse_off = TRUE;
3067 DrawLaser(0, DL_LASER_DISABLED);
3068 DrawGraphic_MM(ELX, ELY, IMG_MM_FUSE);
3071 if (element == EL_BALL_GRAY && CT > 1500)
3073 static int new_elements[] =
3076 EL_MIRROR_FIXED_START,
3078 EL_POLAR_CROSS_START,
3084 int num_new_elements = sizeof(new_elements) / sizeof(int);
3085 int new_element = new_elements[RND(num_new_elements)];
3087 Store[ELX][ELY] = new_element + RND(get_num_elements(new_element));
3088 Feld[ELX][ELY] = EL_GRAY_BALL_OPENING;
3090 /* !!! CHECK AGAIN: Laser on Polarizer !!! */
3101 element = EL_MIRROR_START + RND(16);
3107 element = (rnd == 0 ? EL_KETTLE : rnd == 1 ? EL_BOMB : EL_PRISM);
3114 element = (rnd == 0 ? EL_FUSE_ON :
3115 rnd >= 1 && rnd <= 4 ? EL_PACMAN_RIGHT + rnd - 1 :
3116 rnd >= 5 && rnd <= 20 ? EL_POLAR_START + rnd - 5 :
3117 rnd >= 21 && rnd <= 24 ? EL_POLAR_CROSS_START + rnd - 21 :
3118 EL_MIRROR_FIXED_START + rnd - 25);
3123 graphic = el2gfx(element);
3125 for (i = 0; i < 50; i++)
3131 BlitBitmap(pix[PIX_BACK], drawto,
3132 SX + (graphic % GFX_PER_LINE) * TILEX + x,
3133 SY + (graphic / GFX_PER_LINE) * TILEY + y, 6, 6,
3134 SX + ELX * TILEX + x,
3135 SY + ELY * TILEY + y);
3137 MarkTileDirty(ELX, ELY);
3140 DrawLaser(0, DL_LASER_ENABLED);
3145 Feld[ELX][ELY] = element;
3146 DrawField_MM(ELX, ELY);
3149 printf("NEW ELEMENT: (%d, %d)\n", ELX, ELY);
3152 /* above stuff: GRAY BALL -> PRISM !!! */
3154 LX = ELX * TILEX + 14;
3155 LY = ELY * TILEY + 14;
3156 if (laser.current_angle == (laser.current_angle >> 1) << 1)
3163 laser.num_edges -= 2;
3164 laser.num_damages--;
3168 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
3169 if (laser.damage[i].is_mirror)
3173 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3175 DrawLaser(0, DL_LASER_DISABLED);
3177 DrawLaser(0, DL_LASER_DISABLED);
3183 printf("TEST ELEMENT: %d\n", Feld[0][0]);
3190 if (IS_WALL_ICE(element) && CT > 1000)
3192 PlaySoundStereo(SND_SLURP, ST(ELX));
3195 Feld[ELX][ELY] = Feld[ELX][ELY] - EL_WALL_ICE + EL_WALL_CHANGING;
3196 Store[ELX][ELY] = EL_WALL_ICE;
3197 Store2[ELX][ELY] = laser.wall_mask;
3199 laser.dest_element = Feld[ELX][ELY];
3204 for (i = 0; i < 5; i++)
3210 Feld[ELX][ELY] &= (laser.wall_mask ^ 0xFF);
3214 DrawWallsAnimation_MM(ELX, ELY, Feld[ELX][ELY], phase, laser.wall_mask);
3219 if (Feld[ELX][ELY] == EL_WALL_ICE)
3220 Feld[ELX][ELY] = EL_EMPTY;
3224 LX = laser.edge[laser.num_edges].x - (SX + 2);
3225 LY = laser.edge[laser.num_edges].y - (SY + 2);
3228 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
3229 if (laser.damage[i].is_mirror)
3233 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3235 DrawLaser(0, DL_LASER_DISABLED);
3242 if (IS_WALL_AMOEBA(element) && CT > 1200)
3244 int k1, k2, k3, dx, dy, de, dm;
3245 int element2 = Feld[ELX][ELY];
3247 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element))
3250 for (i = laser.num_damages - 1; i >= 0; i--)
3251 if (laser.damage[i].is_mirror)
3254 r = laser.num_edges;
3255 d = laser.num_damages;
3262 DrawLaser(laser.damage[k1].edge - 1, DL_LASER_DISABLED);
3265 DrawLaser(0, DL_LASER_ENABLED);
3268 x = laser.damage[k1].x;
3269 y = laser.damage[k1].y;
3274 for (i = 0; i < 4; i++)
3276 if (laser.wall_mask & (1 << i))
3278 if (ReadPixel(drawto,
3279 SX + ELX * TILEX + 14 + (i % 2) * 2,
3280 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3282 if (ReadPixel(drawto,
3283 SX + ELX * TILEX + 31 * (i % 2),
3284 SY + ELY * TILEY + 14 + (i / 2) * 2) == pen_ray)
3291 for (i = 0; i < 4; i++)
3293 if (laser.wall_mask & (1 << i))
3295 if (ReadPixel(drawto,
3296 SX + ELX * TILEX + 31 * (i % 2),
3297 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3304 if (laser.num_beamers > 0 ||
3305 k1 < 1 || k2 < 4 || k3 < 4 ||
3306 ReadPixel(drawto, SX + ELX * TILEX + 14, SY + ELY * TILEY + 14)
3309 laser.num_edges = r;
3310 laser.num_damages = d;
3312 DrawLaser(0, DL_LASER_DISABLED);
3315 Feld[ELX][ELY] = element | laser.wall_mask;
3319 de = Feld[ELX][ELY];
3320 dm = laser.wall_mask;
3324 int x = ELX, y = ELY;
3325 int wall_mask = laser.wall_mask;
3328 DrawLaser(0, DL_LASER_ENABLED);
3330 PlaySoundStereo(SND_AMOEBE, ST(dx));
3332 Feld[x][y] = Feld[x][y] - EL_WALL_AMOEBA + EL_WALL_CHANGING;
3333 Store[x][y] = EL_WALL_AMOEBA;
3334 Store2[x][y] = wall_mask;
3340 DrawWallsAnimation_MM(dx, dy, de, 4, dm);
3342 DrawLaser(0, DL_LASER_ENABLED);
3344 PlaySoundStereo(SND_AMOEBE, ST(dx));
3346 for (i = 4; i >= 0; i--)
3348 DrawWallsAnimation_MM(dx, dy, de, i, dm);
3354 DrawLaser(0, DL_LASER_ENABLED);
3359 if ((element == EL_BLOCK_WOOD || element == EL_BLOCK_STONE) &&
3360 laser.stops_inside_element && CT > 1500)
3365 if (ABS(XS) > ABS(YS))
3372 for (i = 0; i < 4; i++)
3379 x = ELX + Step[k * 4].x;
3380 y = ELY + Step[k * 4].y;
3382 if (!IN_LEV_FIELD(x, y) || Feld[x][y] != EL_EMPTY)
3385 if (ObjHit(x, y, HIT_POS_CENTER | HIT_POS_EDGE | HIT_POS_BETWEEN))
3393 laser.overloaded = (element == EL_BLOCK_STONE);
3398 PlaySoundStereo(SND_BONG, ST(ELX));
3401 Feld[x][y] = element;
3403 DrawGraphic_MM(ELX, ELY, IMG_EMPTY);
3406 if (element == EL_BLOCK_STONE && Box[ELX][ELY])
3408 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
3409 DrawLaser(laser.num_edges - 1, DL_LASER_ENABLED);
3417 if (element == EL_FUEL_FULL && CT > 200)
3419 for (i = game_mm.energy_left; i <= MAX_LASER_ENERGY; i+=2)
3422 BlitBitmap(pix[PIX_DOOR], drawto,
3423 DOOR_GFX_PAGEX4 + XX_ENERGY,
3424 DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
3425 ENERGY_XSIZE, i, DX_ENERGY,
3426 DY_ENERGY + ENERGY_YSIZE - i);
3429 redraw_mask |= REDRAW_DOOR_1;
3435 game_mm.energy_left = MAX_LASER_ENERGY;
3436 Feld[ELX][ELY] = EL_FUEL_EMPTY;
3437 DrawField_MM(ELX, ELY);
3439 DrawLaser(0, DL_LASER_ENABLED);
3447 void GameActions_MM(byte action[MAX_PLAYERS], boolean warp_mode)
3450 ClickElement(0, 0, MB_NOT_PRESSED);
3452 GameActions_MM_Ext(action, warp_mode);
3458 int mx, my, ox, oy, nx, ny;
3462 if (++p >= game_mm.num_pacman)
3465 game_mm.pacman[p].dir--;
3467 for (l = 1; l < 5; l++)
3469 game_mm.pacman[p].dir++;
3471 if (game_mm.pacman[p].dir > 4)
3472 game_mm.pacman[p].dir = 1;
3474 if (game_mm.pacman[p].dir % 2)
3477 my = game_mm.pacman[p].dir - 2;
3482 mx = 3 - game_mm.pacman[p].dir;
3485 ox = game_mm.pacman[p].x;
3486 oy = game_mm.pacman[p].y;
3489 element = Feld[nx][ny];
3491 if (nx < 0 || nx > 15 || ny < 0 || ny > 11)
3494 if (!IS_EATABLE4PACMAN(element))
3497 if (ObjHit(nx, ny, HIT_POS_CENTER))
3500 Feld[ox][oy] = EL_EMPTY;
3502 EL_PACMAN_RIGHT - 1 +
3503 (game_mm.pacman[p].dir - 1 +
3504 (game_mm.pacman[p].dir % 2) * 2);
3506 game_mm.pacman[p].x = nx;
3507 game_mm.pacman[p].y = ny;
3509 DrawGraphic_MM(ox, oy, IMG_EMPTY);
3511 if (element != EL_EMPTY)
3513 int graphic = el2gfx(Feld[nx][ny]);
3518 getGraphicSource(graphic, 0, &bitmap, &src_x, &src_y);
3521 ox = SX + ox * TILEX;
3522 oy = SY + oy * TILEY;
3524 for (i = 1; i < 33; i += 2)
3525 BlitBitmap(bitmap, window,
3526 src_x, src_y, TILEX, TILEY,
3527 ox + i * mx, oy + i * my);
3528 Ct = Ct + Counter() - CT;
3531 DrawField_MM(nx, ny);
3534 if (!laser.fuse_off)
3536 DrawLaser(0, DL_LASER_ENABLED);
3538 if (ObjHit(nx, ny, HIT_POS_BETWEEN))
3540 AddDamagedField(nx, ny);
3542 laser.damage[laser.num_damages - 1].edge = 0;
3546 if (element == EL_BOMB)
3547 DeletePacMan(nx, ny);
3549 if (IS_WALL_AMOEBA(element) &&
3550 (LX + 2 * XS) / TILEX == nx &&
3551 (LY + 2 * YS) / TILEY == ny)
3564 boolean raise_level = FALSE;
3567 if (local_player->MovPos)
3570 local_player->LevelSolved = FALSE;
3573 if (game_mm.energy_left)
3575 if (setup.sound_loops)
3576 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3577 SND_CTRL_PLAY_LOOP);
3579 while (game_mm.energy_left > 0)
3581 if (!setup.sound_loops)
3582 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3585 if (game_mm.energy_left > 0 && !(game_mm.energy_left % 10))
3586 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3591 game_mm.energy_left--;
3592 if (game_mm.energy_left >= 0)
3595 BlitBitmap(pix[PIX_DOOR], drawto,
3596 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
3597 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
3598 DX_ENERGY, DY_ENERGY);
3600 redraw_mask |= REDRAW_DOOR_1;
3607 if (setup.sound_loops)
3608 StopSound(SND_SIRR);
3610 else if (native_mm_level.time == 0) /* level without time limit */
3612 if (setup.sound_loops)
3613 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3614 SND_CTRL_PLAY_LOOP);
3616 while (TimePlayed < 999)
3618 if (!setup.sound_loops)
3619 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3620 if (TimePlayed < 999 && !(TimePlayed % 10))
3621 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3622 if (TimePlayed < 900 && !(TimePlayed % 10))
3628 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
3635 if (setup.sound_loops)
3636 StopSound(SND_SIRR);
3643 CloseDoor(DOOR_CLOSE_1);
3645 Request("Level solved !", REQ_CONFIRM);
3647 if (level_nr == leveldir_current->handicap_level)
3649 leveldir_current->handicap_level++;
3650 SaveLevelSetup_SeriesInfo();
3653 if (level_editor_test_game)
3654 game_mm.score = -1; /* no highscore when playing from editor */
3655 else if (level_nr < leveldir_current->last_level)
3656 raise_level = TRUE; /* advance to next level */
3658 if ((hi_pos = NewHiScore_MM()) >= 0)
3660 game_status = HALLOFFAME;
3662 // DrawHallOfFame(hi_pos);
3669 game_status = MAINMENU;
3685 // LoadScore(level_nr);
3687 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
3688 game_mm.score < highscore[MAX_SCORE_ENTRIES - 1].Score)
3691 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3693 if (game_mm.score > highscore[k].Score)
3695 /* player has made it to the hall of fame */
3697 if (k < MAX_SCORE_ENTRIES - 1)
3699 int m = MAX_SCORE_ENTRIES - 1;
3702 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3703 if (!strcmp(setup.player_name, highscore[l].Name))
3705 if (m == k) /* player's new highscore overwrites his old one */
3709 for (l = m; l>k; l--)
3711 strcpy(highscore[l].Name, highscore[l - 1].Name);
3712 highscore[l].Score = highscore[l - 1].Score;
3719 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3720 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3721 highscore[k].Score = game_mm.score;
3728 else if (!strncmp(setup.player_name, highscore[k].Name,
3729 MAX_PLAYER_NAME_LEN))
3730 break; /* player already there with a higher score */
3735 // if (position >= 0)
3736 // SaveScore(level_nr);
3741 static void InitMovingField_MM(int x, int y, int direction)
3743 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3744 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3746 MovDir[x][y] = direction;
3747 MovDir[newx][newy] = direction;
3749 if (Feld[newx][newy] == EL_EMPTY)
3750 Feld[newx][newy] = EL_BLOCKED;
3753 static void Moving2Blocked_MM(int x, int y, int *goes_to_x, int *goes_to_y)
3755 int direction = MovDir[x][y];
3756 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3757 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3763 static void Blocked2Moving_MM(int x, int y,
3764 int *comes_from_x, int *comes_from_y)
3766 int oldx = x, oldy = y;
3767 int direction = MovDir[x][y];
3769 if (direction == MV_LEFT)
3771 else if (direction == MV_RIGHT)
3773 else if (direction == MV_UP)
3775 else if (direction == MV_DOWN)
3778 *comes_from_x = oldx;
3779 *comes_from_y = oldy;
3782 static int MovingOrBlocked2Element_MM(int x, int y)
3784 int element = Feld[x][y];
3786 if (element == EL_BLOCKED)
3790 Blocked2Moving_MM(x, y, &oldx, &oldy);
3792 return Feld[oldx][oldy];
3799 static void RemoveField(int x, int y)
3801 Feld[x][y] = EL_EMPTY;
3808 static void RemoveMovingField_MM(int x, int y)
3810 int oldx = x, oldy = y, newx = x, newy = y;
3812 if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
3815 if (IS_MOVING(x, y))
3817 Moving2Blocked_MM(x, y, &newx, &newy);
3818 if (Feld[newx][newy] != EL_BLOCKED)
3821 else if (Feld[x][y] == EL_BLOCKED)
3823 Blocked2Moving_MM(x, y, &oldx, &oldy);
3824 if (!IS_MOVING(oldx, oldy))
3828 Feld[oldx][oldy] = EL_EMPTY;
3829 Feld[newx][newy] = EL_EMPTY;
3830 MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
3831 MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
3833 DrawLevelField_MM(oldx, oldy);
3834 DrawLevelField_MM(newx, newy);
3837 void PlaySoundLevel(int x, int y, int sound_nr)
3839 int sx = SCREENX(x), sy = SCREENY(y);
3841 int silence_distance = 8;
3843 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
3844 (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
3847 if (!IN_LEV_FIELD(x, y) ||
3848 sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
3849 sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
3852 volume = SOUND_MAX_VOLUME;
3855 stereo = (sx - SCR_FIELDX/2) * 12;
3857 stereo = SOUND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
3858 if (stereo > SOUND_MAX_RIGHT)
3859 stereo = SOUND_MAX_RIGHT;
3860 if (stereo < SOUND_MAX_LEFT)
3861 stereo = SOUND_MAX_LEFT;
3864 if (!IN_SCR_FIELD(sx, sy))
3866 int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
3867 int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
3869 volume -= volume * (dx > dy ? dx : dy) / silence_distance;
3872 PlaySoundExt(sound_nr, volume, stereo, SND_CTRL_PLAY_SOUND);
3875 static void RaiseScore_MM(int value)
3877 game_mm.score += value;
3880 DrawText(DX_SCORE, DY_SCORE, int2str(game_mm.score, 4),
3885 void RaiseScoreElement_MM(int element)
3890 RaiseScore_MM(native_mm_level.score[SC_PACMAN]);
3894 RaiseScore_MM(native_mm_level.score[SC_KEY]);