1 // ============================================================================
2 // Mirror Magic -- McDuffin's Revenge
3 // ----------------------------------------------------------------------------
4 // (c) 1994-2017 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
18 /* graphic position values for game controls */
19 #define ENERGY_XSIZE 32
20 #define ENERGY_YSIZE MAX_LASER_ENERGY
21 #define OVERLOAD_XSIZE ENERGY_XSIZE
22 #define OVERLOAD_YSIZE MAX_LASER_OVERLOAD
24 /* values for Explode_MM() */
25 #define EX_PHASE_START 0
30 /* special positions in the game control window (relative to control window) */
39 #define XX_OVERLOAD 60
40 #define YY_OVERLOAD YY_ENERGY
42 /* special positions in the game control window (relative to main window) */
43 #define DX_LEVEL (DX + XX_LEVEL)
44 #define DY_LEVEL (DY + YY_LEVEL)
45 #define DX_KETTLES (DX + XX_KETTLES)
46 #define DY_KETTLES (DY + YY_KETTLES)
47 #define DX_SCORE (DX + XX_SCORE)
48 #define DY_SCORE (DY + YY_SCORE)
49 #define DX_ENERGY (DX + XX_ENERGY)
50 #define DY_ENERGY (DY + YY_ENERGY)
51 #define DX_OVERLOAD (DX + XX_OVERLOAD)
52 #define DY_OVERLOAD (DY + YY_OVERLOAD)
54 #define IS_LOOP_SOUND(s) ((s) == SND_FUEL)
55 #define IS_MUSIC_SOUND(s) ((s) == SND_TYGER || (s) == SND_VOYAGER)
57 /* game button identifiers */
58 #define GAME_CTRL_ID_LEFT 0
59 #define GAME_CTRL_ID_MIDDLE 1
60 #define GAME_CTRL_ID_RIGHT 2
62 #define NUM_GAME_BUTTONS 3
64 /* values for DrawLaser() */
65 #define DL_LASER_DISABLED 0
66 #define DL_LASER_ENABLED 1
68 /* values for 'click_delay_value' in ClickElement() */
69 #define CLICK_DELAY_SHORT 125
70 #define CLICK_DELAY_LONG 250
71 #define AUTO_ROTATE_DELAY CLICK_DELAY_SHORT
73 /* forward declaration for internal use */
74 static int MovingOrBlocked2Element_MM(int, int);
75 static void Bang_MM(int, int);
76 static void RaiseScore_MM(int);
77 static void RemoveMovingField_MM(int, int);
78 static void InitMovingField_MM(int, int, int);
79 static void ContinueMoving_MM(int, int);
80 static void Moving2Blocked_MM(int, int, int *, int *);
83 static int get_element_angle(int element)
85 int element_phase = get_element_phase(element);
87 if (IS_MIRROR_FIXED(element) ||
88 IS_MCDUFFIN(element) ||
91 return 4 * element_phase;
96 static int get_opposite_angle(int angle)
98 int opposite_angle = angle + ANG_RAY_180;
100 /* make sure "opposite_angle" is in valid interval [0, 15] */
101 return (opposite_angle + 16) % 16;
104 static int get_mirrored_angle(int laser_angle, int mirror_angle)
106 int reflected_angle = 16 - laser_angle + mirror_angle;
108 /* make sure "reflected_angle" is in valid interval [0, 15] */
109 return (reflected_angle + 16) % 16;
112 static void InitMovDir_MM(int x, int y)
114 int element = Feld[x][y];
115 static int direction[3][4] =
117 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
118 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
119 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
124 case EL_PACMAN_RIGHT:
128 Feld[x][y] = EL_PACMAN;
129 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
137 static void InitField(int x, int y, boolean init_game)
139 int element = Feld[x][y];
144 Feld[x][y] = EL_EMPTY;
149 if (native_mm_level.auto_count_kettles)
150 game_mm.kettles_still_needed++;
153 case EL_LIGHTBULB_OFF:
154 game_mm.lights_still_needed++;
158 if (IS_MIRROR(element) ||
159 IS_BEAMER_OLD(element) ||
160 IS_BEAMER(element) ||
162 IS_POLAR_CROSS(element) ||
163 IS_DF_MIRROR(element) ||
164 IS_DF_MIRROR_AUTO(element) ||
165 IS_GRID_STEEL_AUTO(element) ||
166 IS_GRID_WOOD_AUTO(element) ||
167 IS_FIBRE_OPTIC(element))
169 if (IS_BEAMER_OLD(element))
171 Feld[x][y] = EL_BEAMER_BLUE_START + (element - EL_BEAMER_START);
172 element = Feld[x][y];
175 if (!IS_FIBRE_OPTIC(element))
177 static int steps_grid_auto = 0;
179 if (game_mm.num_cycle == 0) /* initialize cycle steps for grids */
180 steps_grid_auto = RND(16) * (RND(2) ? -1 : +1);
182 if (IS_GRID_STEEL_AUTO(element) ||
183 IS_GRID_WOOD_AUTO(element))
184 game_mm.cycle[game_mm.num_cycle].steps = steps_grid_auto;
186 game_mm.cycle[game_mm.num_cycle].steps = RND(16) * (RND(2) ? -1 : +1);
188 game_mm.cycle[game_mm.num_cycle].x = x;
189 game_mm.cycle[game_mm.num_cycle].y = y;
193 if (IS_BEAMER(element) || IS_FIBRE_OPTIC(element))
195 int beamer_nr = BEAMER_NR(element);
196 int nr = laser.beamer[beamer_nr][0].num;
198 laser.beamer[beamer_nr][nr].x = x;
199 laser.beamer[beamer_nr][nr].y = y;
200 laser.beamer[beamer_nr][nr].num = 1;
203 else if (IS_PACMAN(element))
207 else if (IS_MCDUFFIN(element) || IS_LASER(element))
209 laser.start_edge.x = x;
210 laser.start_edge.y = y;
211 laser.start_angle = get_element_angle(element);
218 static void InitCycleElements()
222 if (game_mm.num_cycle == 0) /* no elements to cycle */
225 for (i = 0; i < 16; i++)
227 for (j = 0; j < game_mm.num_cycle; j++)
229 int x = game_mm.cycle[j].x;
230 int y = game_mm.cycle[j].y;
231 int step = SIGN(game_mm.cycle[j].steps);
232 int last_element = Feld[x][y];
233 int next_element = get_rotated_element(last_element, step);
235 if (!game_mm.cycle[j].steps)
238 Feld[x][y] = next_element;
241 game_mm.cycle[j].steps -= step;
248 if (setup.quick_doors)
252 Delay(AUTO_ROTATE_DELAY);
256 static void InitLaser()
258 int start_element = Feld[laser.start_edge.x][laser.start_edge.y];
259 int step = (IS_LASER(start_element) ? 4 : 0);
261 LX = laser.start_edge.x * TILEX;
262 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
265 LX += (laser.start_angle == ANG_RAY_RIGHT ? 28 + step : 0 - step);
267 LY = laser.start_edge.y * TILEY;
268 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
269 LY += (laser.start_angle == ANG_RAY_DOWN ? 28 + step : 0 - step);
273 XS = 2 * Step[laser.start_angle].x;
274 YS = 2 * Step[laser.start_angle].y;
276 laser.current_angle = laser.start_angle;
278 laser.num_damages = 0;
280 laser.num_beamers = 0;
281 laser.beamer_edge[0] = 0;
283 AddLaserEdge(LX, LY); /* set laser starting edge */
285 pen_ray = GetPixelFromRGB(window,
286 native_mm_level.laser_red * 0xFF,
287 native_mm_level.laser_green * 0xFF,
288 native_mm_level.laser_blue * 0xFF);
291 void InitGameEngine_MM()
295 /* set global game control values */
296 game_mm.num_cycle = 0;
297 game_mm.num_pacman = 0;
300 game_mm.energy_left = 0; // later set to "native_mm_level.time"
301 game_mm.kettles_still_needed =
302 (native_mm_level.auto_count_kettles ? 0 : native_mm_level.kettles_needed);
303 game_mm.lights_still_needed = 0;
304 game_mm.num_keys = 0;
306 game_mm.level_solved = FALSE;
307 game_mm.game_over = FALSE;
308 game_mm.game_over_cause = 0;
310 game_mm.laser_overload_value = 0;
312 /* set global laser control values (must be set before "InitLaser()") */
313 laser.start_edge.x = 0;
314 laser.start_edge.y = 0;
315 laser.start_angle = 0;
317 for (i = 0; i < MAX_NUM_BEAMERS; i++)
318 laser.beamer[i][0].num = laser.beamer[i][1].num = 0;
320 laser.overloaded = FALSE;
321 laser.overload_value = 0;
322 laser.fuse_off = FALSE;
323 laser.fuse_x = laser.fuse_y = -1;
325 laser.dest_element = EL_EMPTY;
330 for (x = 0; x < lev_fieldx; x++)
332 for (y = 0; y < lev_fieldy; y++)
334 Feld[x][y] = Ur[x][y];
335 Hit[x][y] = Box[x][y] = 0;
337 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
338 Store[x][y] = Store2[x][y] = 0;
342 InitField(x, y, TRUE);
347 CloseDoor(DOOR_CLOSE_1);
353 void InitGameActions_MM()
361 /* copy default game door content to main double buffer */
362 BlitBitmap(pix[PIX_DOOR], drawto,
363 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
367 DrawText(DX_LEVEL, DY_LEVEL,
368 int2str(level_nr, 2), FONT_TEXT_2);
369 DrawText(DX_KETTLES, DY_KETTLES,
370 int2str(game_mm.kettles_still_needed, 3), FONT_TEXT_2);
371 DrawText(DX_SCORE, DY_SCORE,
372 int2str(game_mm.score, 4), FONT_TEXT_2);
381 /* copy actual game door content to door double buffer for OpenDoor() */
382 BlitBitmap(drawto, pix[PIX_DB_DOOR],
383 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
387 OpenDoor(DOOR_OPEN_ALL);
390 if (setup.sound_loops)
391 PlaySoundExt(SND_FUEL, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT, SND_CTRL_PLAY_LOOP);
393 for (i = 0; i <= native_mm_level.time; i += 2)
395 if (!setup.sound_loops)
396 PlaySoundStereo(SND_FUEL, SOUND_MAX_RIGHT);
398 game_mm.energy_left = i;
400 UpdateAndDisplayGameControlValues();
407 if (setup.quick_doors)
412 game_mm.energy_left = native_mm_level.time;
414 if (setup.sound_loops)
418 if (setup.sound_music && num_bg_loops)
419 PlayMusic(level_nr % num_bg_loops);
425 void AddLaserEdge(int lx, int ly)
427 if (lx < -2 || ly < -2 || lx >= SXSIZE + 2 || ly >= SYSIZE + 2)
429 Error(ERR_WARN, "AddLaserEdge: out of bounds: %d, %d", lx, ly);
434 laser.edge[laser.num_edges].x = SX + 2 + lx;
435 laser.edge[laser.num_edges].y = SY + 2 + ly;
441 void AddDamagedField(int ex, int ey)
443 laser.damage[laser.num_damages].is_mirror = FALSE;
444 laser.damage[laser.num_damages].angle = laser.current_angle;
445 laser.damage[laser.num_damages].edge = laser.num_edges;
446 laser.damage[laser.num_damages].x = ex;
447 laser.damage[laser.num_damages].y = ey;
457 int last_x = laser.edge[laser.num_edges - 1].x - SX - 2;
458 int last_y = laser.edge[laser.num_edges - 1].y - SY - 2;
460 return ((x - last_x) * XS < 0 || (y - last_y) * YS < 0);
466 static int getMaskFromElement(int element)
468 if (IS_GRID(element))
469 return IMG_MM_MASK_GRID_1 + get_element_phase(element);
470 else if (IS_MCDUFFIN(element))
471 return IMG_MM_MASK_MCDUFFIN_RIGHT + get_element_phase(element);
472 else if (IS_RECTANGLE(element) || IS_DF_GRID(element))
473 return IMG_MM_MASK_RECTANGLE;
475 return IMG_MM_MASK_CIRCLE;
483 printf("ScanPixel: start scanning at (%d, %d) [%d, %d] [%d, %d]\n",
484 LX, LY, LX / TILEX, LY / TILEY, LX % TILEX, LY % TILEY);
487 /* follow laser beam until it hits something (at least the screen border) */
488 while (hit_mask == HIT_MASK_NO_HIT)
494 if (SX + LX < REAL_SX || SX + LX >= REAL_SX + FULL_SXSIZE ||
495 SY + LY < REAL_SY || SY + LY >= REAL_SY + FULL_SYSIZE)
497 printf("ScanPixel: touched screen border!\n");
503 for (i = 0; i < 4; i++)
505 int px = LX + (i % 2) * 2;
506 int py = LY + (i / 2) * 2;
509 int lx = (px + TILEX) / TILEX - 1; /* ...+TILEX...-1 to get correct */
510 int ly = (py + TILEY) / TILEY - 1; /* negative values! */
513 if (IN_LEV_FIELD(lx, ly))
515 int element = Feld[lx][ly];
517 if (element == EL_EMPTY || element == EL_EXPLODING_TRANSP)
521 else if (IS_WALL(element) || IS_WALL_CHANGING(element))
523 int pos = dy / MINI_TILEY * 2 + dx / MINI_TILEX;
525 pixel = ((element & (1 << pos)) ? 1 : 0);
529 int graphic_mask = getMaskFromElement(element);
534 getGraphicSource(graphic_mask, 0, &bitmap, &src_x, &src_y);
539 pixel = (ReadPixel(bitmap, mask_x, mask_y) ? 1 : 0);
544 pixel = (SX + px < REAL_SX || SX + px >= REAL_SX + FULL_SXSIZE ||
545 SY + py < REAL_SY || SY + py >= REAL_SY + FULL_SYSIZE);
548 if ((Sign[laser.current_angle] & (1 << i)) && pixel)
549 hit_mask |= (1 << i);
552 if (hit_mask == HIT_MASK_NO_HIT)
554 /* hit nothing -- go on with another step */
566 int end = 0, rf = laser.num_edges;
568 /* do not scan laser again after the game was lost for whatever reason */
569 if (game_mm.game_over)
572 laser.overloaded = FALSE;
573 laser.stops_inside_element = FALSE;
575 DrawLaser(0, DL_LASER_ENABLED);
578 printf("Start scanning with LX == %d, LY == %d, XS == %d, YS == %d\n",
586 if (laser.num_edges > MAX_LASER_LEN || laser.num_damages > MAX_LASER_LEN)
589 laser.overloaded = TRUE;
594 hit_mask = ScanPixel();
597 printf("Hit something at LX == %d, LY == %d, XS == %d, YS == %d\n",
601 /* hit something -- check out what it was */
602 ELX = (LX + XS) / TILEX;
603 ELY = (LY + YS) / TILEY;
606 printf("hit_mask (1) == '%x' (%d, %d) (%d, %d)\n",
607 hit_mask, LX, LY, ELX, ELY);
610 if (!IN_LEV_FIELD(ELX, ELY) || !IN_PIX_FIELD(LX, LY))
613 laser.dest_element = element;
618 if (hit_mask == (HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT))
620 /* we have hit the top-right and bottom-left element --
621 choose the bottom-left one */
622 /* !!! THIS CAN BE DONE MORE INTELLIGENTLY, FOR EXAMPLE, IF ONE
623 ELEMENT WAS STEEL AND THE OTHER ONE WAS ICE => ALWAYS CHOOSE
624 THE ICE AND MELT IT AWAY INSTEAD OF OVERLOADING LASER !!! */
625 ELX = (LX - 2) / TILEX;
626 ELY = (LY + 2) / TILEY;
629 if (hit_mask == (HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT))
631 /* we have hit the top-left and bottom-right element --
632 choose the top-left one */
633 /* !!! SEE ABOVE !!! */
634 ELX = (LX - 2) / TILEX;
635 ELY = (LY - 2) / TILEY;
639 printf("hit_mask (2) == '%x' (%d, %d) (%d, %d)\n",
640 hit_mask, LX, LY, ELX, ELY);
643 element = Feld[ELX][ELY];
644 laser.dest_element = element;
647 printf("Hit element %d at (%d, %d) [%d, %d] [%d, %d] [%d]\n",
650 LX % TILEX, LY % TILEY,
655 if (!IN_LEV_FIELD(ELX, ELY))
656 printf("WARNING! (1) %d, %d (%d)\n", ELX, ELY, element);
659 if (element == EL_EMPTY)
661 if (!HitOnlyAnEdge(element, hit_mask))
664 else if (element == EL_FUSE_ON)
666 if (HitPolarizer(element, hit_mask))
669 else if (IS_GRID(element) || IS_DF_GRID(element))
671 if (HitPolarizer(element, hit_mask))
674 else if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD ||
675 element == EL_GATE_STONE || element == EL_GATE_WOOD)
677 if (HitBlock(element, hit_mask))
684 else if (IS_MCDUFFIN(element))
686 if (HitLaserSource(element, hit_mask))
689 else if ((element >= EL_EXIT_CLOSED && element <= EL_EXIT_OPEN) ||
690 IS_RECEIVER(element))
692 if (HitLaserDestination(element, hit_mask))
695 else if (IS_WALL(element))
697 if (IS_WALL_STEEL(element) || IS_DF_WALL_STEEL(element))
699 if (HitReflectingWalls(element, hit_mask))
704 if (HitAbsorbingWalls(element, hit_mask))
710 if (HitElement(element, hit_mask))
715 DrawLaser(rf - 1, DL_LASER_ENABLED);
716 rf = laser.num_edges;
720 if (laser.dest_element != Feld[ELX][ELY])
722 printf("ALARM: laser.dest_element == %d, Feld[ELX][ELY] == %d\n",
723 laser.dest_element, Feld[ELX][ELY]);
727 if (!end && !laser.stops_inside_element && !StepBehind())
730 printf("ScanLaser: Go one step back\n");
736 AddLaserEdge(LX, LY);
740 DrawLaser(rf - 1, DL_LASER_ENABLED);
745 if (!IN_LEV_FIELD(ELX, ELY))
746 printf("WARNING! (2) %d, %d\n", ELX, ELY);
750 void DrawLaserExt(int start_edge, int num_edges, int mode)
756 printf("DrawLaserExt: start_edge, num_edges, mode == %d, %d, %d\n",
757 start_edge, num_edges, mode);
762 Error(ERR_WARN, "DrawLaserExt: start_edge < 0");
769 Error(ERR_WARN, "DrawLaserExt: num_edges < 0");
775 if (mode == DL_LASER_DISABLED)
777 printf("DrawLaser: Delete laser from edge %d\n", start_edge);
781 /* now draw the laser to the backbuffer and (if enabled) to the screen */
782 DrawLines(drawto, &laser.edge[start_edge], num_edges,
783 (mode == DL_LASER_ENABLED ? pen_ray : pen_bg));
785 redraw_mask |= REDRAW_FIELD;
787 if (mode == DL_LASER_ENABLED)
790 /* after the laser was deleted, the "damaged" graphics must be restored */
791 if (laser.num_damages)
793 int damage_start = 0;
796 /* determine the starting edge, from which graphics need to be restored */
799 for (i = 0; i < laser.num_damages; i++)
801 if (laser.damage[i].edge == start_edge + 1)
810 /* restore graphics from this starting edge to the end of damage list */
811 for (i = damage_start; i < laser.num_damages; i++)
813 int lx = laser.damage[i].x;
814 int ly = laser.damage[i].y;
815 int element = Feld[lx][ly];
817 if (Hit[lx][ly] == laser.damage[i].edge)
818 if (!((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
821 if (Box[lx][ly] == laser.damage[i].edge)
824 if (IS_DRAWABLE(element))
825 DrawField_MM(lx, ly);
828 elx = laser.damage[damage_start].x;
829 ely = laser.damage[damage_start].y;
830 element = Feld[elx][ely];
833 if (IS_BEAMER(element))
837 for (i = 0; i < laser.num_beamers; i++)
838 printf("-> %d\n", laser.beamer_edge[i]);
839 printf("DrawLaserExt: IS_BEAMER: [%d]: Hit[%d][%d] == %d [%d]\n",
840 mode, elx, ely, Hit[elx][ely], start_edge);
841 printf("DrawLaserExt: IS_BEAMER: %d / %d\n",
842 get_element_angle(element), laser.damage[damage_start].angle);
846 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
847 laser.num_beamers > 0 &&
848 start_edge == laser.beamer_edge[laser.num_beamers - 1])
850 /* element is outgoing beamer */
851 laser.num_damages = damage_start + 1;
853 if (IS_BEAMER(element))
854 laser.current_angle = get_element_angle(element);
858 /* element is incoming beamer or other element */
859 laser.num_damages = damage_start;
860 laser.current_angle = laser.damage[laser.num_damages].angle;
865 /* no damages but McDuffin himself (who needs to be redrawn anyway) */
867 elx = laser.start_edge.x;
868 ely = laser.start_edge.y;
869 element = Feld[elx][ely];
872 laser.num_edges = start_edge + 1;
874 laser.current_angle = laser.start_angle;
876 LX = laser.edge[start_edge].x - (SX + 2);
877 LY = laser.edge[start_edge].y - (SY + 2);
878 XS = 2 * Step[laser.current_angle].x;
879 YS = 2 * Step[laser.current_angle].y;
882 printf("DrawLaser: Set (LX, LY) to (%d, %d) [%d]\n",
888 if (IS_BEAMER(element) ||
889 IS_FIBRE_OPTIC(element) ||
890 IS_PACMAN(element) ||
892 IS_POLAR_CROSS(element) ||
893 element == EL_FUSE_ON)
898 printf("element == %d\n", element);
901 if (IS_22_5_ANGLE(laser.current_angle)) /* neither 90° nor 45° angle */
902 step_size = ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) ? 4 : 3);
906 if (IS_POLAR(element) || IS_POLAR_CROSS(element) ||
907 ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
908 (laser.num_beamers == 0 ||
909 start_edge != laser.beamer_edge[laser.num_beamers - 1])))
911 /* element is incoming beamer or other element */
912 step_size = -step_size;
917 if (IS_BEAMER(element))
919 printf("start_edge == %d, laser.beamer_edge == %d\n",
920 start_edge, laser.beamer_edge);
924 LX += step_size * XS;
925 LY += step_size * YS;
927 else if (element != EL_EMPTY)
936 printf("DrawLaser: Finally: (LX, LY) to (%d, %d) [%d]\n",
941 void DrawLaser(int start_edge, int mode)
943 if (laser.num_edges - start_edge < 0)
945 Error(ERR_WARN, "DrawLaser: laser.num_edges - start_edge < 0");
950 /* check if laser is interrupted by beamer element */
951 if (laser.num_beamers > 0 &&
952 start_edge < laser.beamer_edge[laser.num_beamers - 1])
954 if (mode == DL_LASER_ENABLED)
957 int tmp_start_edge = start_edge;
959 /* draw laser segments forward from the start to the last beamer */
960 for (i = 0; i < laser.num_beamers; i++)
962 int tmp_num_edges = laser.beamer_edge[i] - tmp_start_edge;
964 if (tmp_num_edges <= 0)
968 printf("DrawLaser: DL_LASER_ENABLED: i==%d: %d, %d\n",
969 i, laser.beamer_edge[i], tmp_start_edge);
972 DrawLaserExt(tmp_start_edge, tmp_num_edges, DL_LASER_ENABLED);
974 tmp_start_edge = laser.beamer_edge[i];
977 /* draw last segment from last beamer to the end */
978 DrawLaserExt(tmp_start_edge, laser.num_edges - tmp_start_edge,
984 int last_num_edges = laser.num_edges;
985 int num_beamers = laser.num_beamers;
987 /* delete laser segments backward from the end to the first beamer */
988 for (i = num_beamers-1; i >= 0; i--)
990 int tmp_num_edges = last_num_edges - laser.beamer_edge[i];
992 if (laser.beamer_edge[i] - start_edge <= 0)
995 DrawLaserExt(laser.beamer_edge[i], tmp_num_edges, DL_LASER_DISABLED);
997 last_num_edges = laser.beamer_edge[i];
1002 if (last_num_edges - start_edge <= 0)
1003 printf("DrawLaser: DL_LASER_DISABLED: %d, %d\n",
1004 last_num_edges, start_edge);
1007 /* delete first segment from start to the first beamer */
1008 DrawLaserExt(start_edge, last_num_edges - start_edge, DL_LASER_DISABLED);
1013 DrawLaserExt(start_edge, laser.num_edges - start_edge, mode);
1017 boolean HitElement(int element, int hit_mask)
1019 if (HitOnlyAnEdge(element, hit_mask))
1022 if (IS_MOVING(ELX, ELY) || IS_BLOCKED(ELX, ELY))
1023 element = MovingOrBlocked2Element_MM(ELX, ELY);
1026 printf("HitElement (1): element == %d\n", element);
1030 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1031 printf("HitElement (%d): EXACT MATCH @ (%d, %d)\n", element, ELX, ELY);
1033 printf("HitElement (%d): FUZZY MATCH @ (%d, %d)\n", element, ELX, ELY);
1036 AddDamagedField(ELX, ELY);
1038 /* this is more precise: check if laser would go through the center */
1039 if ((ELX * TILEX + 14 - LX) * YS != (ELY * TILEY + 14 - LY) * XS)
1041 /* skip the whole element before continuing the scan */
1047 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1049 if (LX/TILEX > ELX || LY/TILEY > ELY)
1051 /* skipping scan positions to the right and down skips one scan
1052 position too much, because this is only the top left scan position
1053 of totally four scan positions (plus one to the right, one to the
1054 bottom and one to the bottom right) */
1064 printf("HitElement (2): element == %d\n", element);
1067 if (LX + 5 * XS < 0 ||
1077 printf("HitElement (3): element == %d\n", element);
1080 if (IS_POLAR(element) &&
1081 ((element - EL_POLAR_START) % 2 ||
1082 (element - EL_POLAR_START) / 2 != laser.current_angle % 8))
1084 PlaySoundStereo(SND_KINK, ST(ELX));
1086 laser.num_damages--;
1091 if (IS_POLAR_CROSS(element) &&
1092 (element - EL_POLAR_CROSS_START) != laser.current_angle % 4)
1094 PlaySoundStereo(SND_KINK, ST(ELX));
1096 laser.num_damages--;
1101 if (!IS_BEAMER(element) &&
1102 !IS_FIBRE_OPTIC(element) &&
1103 !IS_GRID_WOOD(element) &&
1104 element != EL_FUEL_EMPTY)
1107 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1108 printf("EXACT MATCH @ (%d, %d)\n", ELX, ELY);
1110 printf("FUZZY MATCH @ (%d, %d)\n", ELX, ELY);
1113 LX = ELX * TILEX + 14;
1114 LY = ELY * TILEY + 14;
1116 AddLaserEdge(LX, LY);
1119 if (IS_MIRROR(element) ||
1120 IS_MIRROR_FIXED(element) ||
1121 IS_POLAR(element) ||
1122 IS_POLAR_CROSS(element) ||
1123 IS_DF_MIRROR(element) ||
1124 IS_DF_MIRROR_AUTO(element) ||
1125 element == EL_PRISM ||
1126 element == EL_REFRACTOR)
1128 int current_angle = laser.current_angle;
1131 laser.num_damages--;
1133 AddDamagedField(ELX, ELY);
1135 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1138 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1140 if (IS_MIRROR(element) ||
1141 IS_MIRROR_FIXED(element) ||
1142 IS_DF_MIRROR(element) ||
1143 IS_DF_MIRROR_AUTO(element))
1144 laser.current_angle = get_mirrored_angle(laser.current_angle,
1145 get_element_angle(element));
1147 if (element == EL_PRISM || element == EL_REFRACTOR)
1148 laser.current_angle = RND(16);
1150 XS = 2 * Step[laser.current_angle].x;
1151 YS = 2 * Step[laser.current_angle].y;
1153 if (!IS_22_5_ANGLE(laser.current_angle)) /* 90° or 45° angle */
1158 LX += step_size * XS;
1159 LY += step_size * YS;
1162 /* draw sparkles on mirror */
1163 if ((IS_MIRROR(element) || IS_MIRROR_FIXED(element)) &&
1164 current_angle != laser.current_angle)
1166 MoveSprite(vp, &Pfeil[2], 4 + 16 * ELX, 5 + 16 * ELY + 1);
1170 if ((!IS_POLAR(element) && !IS_POLAR_CROSS(element)) &&
1171 current_angle != laser.current_angle)
1172 PlaySoundStereo(SND_LASER, ST(ELX));
1175 (get_opposite_angle(laser.current_angle) ==
1176 laser.damage[laser.num_damages - 1].angle ? TRUE : FALSE);
1178 return (laser.overloaded ? TRUE : FALSE);
1181 if (element == EL_FUEL_FULL)
1183 laser.stops_inside_element = TRUE;
1188 if (element == EL_BOMB || element == EL_MINE)
1190 PlaySoundStereo(SND_KINK, ST(ELX));
1192 if (element == EL_MINE)
1193 laser.overloaded = TRUE;
1196 if (element == EL_KETTLE ||
1197 element == EL_CELL ||
1198 element == EL_KEY ||
1199 element == EL_LIGHTBALL ||
1200 element == EL_PACMAN ||
1203 if (!IS_PACMAN(element))
1206 if (element == EL_PACMAN)
1209 if (element == EL_KETTLE || element == EL_CELL)
1211 if (game_mm.kettles_still_needed > 0)
1212 game_mm.kettles_still_needed--;
1216 if (game_mm.kettles_still_needed == 0)
1219 static int xy[4][2] =
1227 PlaySoundStereo(SND_KLING, ST(ELX));
1229 for (y = 0; y < lev_fieldy; y++)
1231 for (x = 0; x < lev_fieldx; x++)
1233 /* initiate opening animation of exit door */
1234 if (Feld[x][y] == EL_EXIT_CLOSED)
1235 Feld[x][y] = EL_EXIT_OPENING;
1237 /* remove field that blocks receiver */
1238 if (IS_RECEIVER(Feld[x][y]))
1240 int phase = Feld[x][y] - EL_RECEIVER_START;
1241 int blocking_x, blocking_y;
1243 blocking_x = x + xy[phase][0];
1244 blocking_y = y + xy[phase][1];
1246 if (IN_LEV_FIELD(blocking_x, blocking_y))
1248 Feld[blocking_x][blocking_y] = EL_EMPTY;
1250 DrawField_MM(blocking_x, blocking_y);
1256 DrawLaser(0, DL_LASER_ENABLED);
1259 else if (element == EL_KEY)
1263 else if (element == EL_LIGHTBALL)
1267 else if (IS_PACMAN(element))
1269 DeletePacMan(ELX, ELY);
1276 if (element == EL_LIGHTBULB_OFF || element == EL_LIGHTBULB_ON)
1278 PlaySoundStereo(SND_KINK, ST(ELX));
1280 DrawLaser(0, DL_LASER_ENABLED);
1282 if (Feld[ELX][ELY] == EL_LIGHTBULB_OFF)
1284 Feld[ELX][ELY] = EL_LIGHTBULB_ON;
1285 game_mm.lights_still_needed--;
1289 Feld[ELX][ELY] = EL_LIGHTBULB_OFF;
1290 game_mm.lights_still_needed++;
1293 DrawField_MM(ELX, ELY);
1294 DrawLaser(0, DL_LASER_ENABLED);
1299 laser.stops_inside_element = TRUE;
1305 printf("HitElement (4): element == %d\n", element);
1308 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1309 laser.num_beamers < MAX_NUM_BEAMERS &&
1310 laser.beamer[BEAMER_NR(element)][1].num)
1312 int beamer_angle = get_element_angle(element);
1313 int beamer_nr = BEAMER_NR(element);
1317 printf("HitElement (BEAMER): element == %d\n", element);
1320 laser.num_damages--;
1322 if (IS_FIBRE_OPTIC(element) ||
1323 laser.current_angle == get_opposite_angle(beamer_angle))
1327 LX = ELX * TILEX + 14;
1328 LY = ELY * TILEY + 14;
1330 AddLaserEdge(LX, LY);
1331 AddDamagedField(ELX, ELY);
1333 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1336 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1338 pos = (ELX == laser.beamer[beamer_nr][0].x &&
1339 ELY == laser.beamer[beamer_nr][0].y ? 1 : 0);
1340 ELX = laser.beamer[beamer_nr][pos].x;
1341 ELY = laser.beamer[beamer_nr][pos].y;
1342 LX = ELX * TILEX + 14;
1343 LY = ELY * TILEY + 14;
1345 if (IS_BEAMER(element))
1347 laser.current_angle = get_element_angle(Feld[ELX][ELY]);
1348 XS = 2 * Step[laser.current_angle].x;
1349 YS = 2 * Step[laser.current_angle].y;
1352 laser.beamer_edge[laser.num_beamers] = laser.num_edges;
1354 AddLaserEdge(LX, LY);
1355 AddDamagedField(ELX, ELY);
1357 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1360 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1362 if (laser.current_angle == (laser.current_angle >> 1) << 1)
1367 LX += step_size * XS;
1368 LY += step_size * YS;
1370 laser.num_beamers++;
1379 boolean HitOnlyAnEdge(int element, int hit_mask)
1381 /* check if the laser hit only the edge of an element and, if so, go on */
1384 printf("LX, LY, hit_mask == %d, %d, %d\n", LX, LY, hit_mask);
1387 if ((hit_mask == HIT_MASK_TOPLEFT ||
1388 hit_mask == HIT_MASK_TOPRIGHT ||
1389 hit_mask == HIT_MASK_BOTTOMLEFT ||
1390 hit_mask == HIT_MASK_BOTTOMRIGHT) &&
1391 laser.current_angle % 4) /* angle is not 90° */
1395 if (hit_mask == HIT_MASK_TOPLEFT)
1400 else if (hit_mask == HIT_MASK_TOPRIGHT)
1405 else if (hit_mask == HIT_MASK_BOTTOMLEFT)
1410 else /* (hit_mask == HIT_MASK_BOTTOMRIGHT) */
1416 AddDamagedField((LX + 2 * dx) / TILEX, (LY + 2 * dy) / TILEY);
1422 printf("[HitOnlyAnEdge() == TRUE]\n");
1429 printf("[HitOnlyAnEdge() == FALSE]\n");
1435 boolean HitPolarizer(int element, int hit_mask)
1437 if (HitOnlyAnEdge(element, hit_mask))
1440 if (IS_DF_GRID(element))
1442 int grid_angle = get_element_angle(element);
1445 printf("HitPolarizer: angle: grid == %d, laser == %d\n",
1446 grid_angle, laser.current_angle);
1449 AddLaserEdge(LX, LY);
1450 AddDamagedField(ELX, ELY);
1453 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1455 if (laser.current_angle == grid_angle ||
1456 laser.current_angle == get_opposite_angle(grid_angle))
1458 /* skip the whole element before continuing the scan */
1464 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1466 if (LX/TILEX > ELX || LY/TILEY > ELY)
1468 /* skipping scan positions to the right and down skips one scan
1469 position too much, because this is only the top left scan position
1470 of totally four scan positions (plus one to the right, one to the
1471 bottom and one to the bottom right) */
1477 AddLaserEdge(LX, LY);
1483 printf("HitPolarizer: LX, LY == %d, %d [%d, %d] [%d, %d]\n",
1485 LX / TILEX, LY / TILEY,
1486 LX % TILEX, LY % TILEY);
1491 else if (IS_GRID_STEEL_FIXED(element) || IS_GRID_STEEL_AUTO(element))
1493 return HitReflectingWalls(element, hit_mask);
1497 return HitAbsorbingWalls(element, hit_mask);
1500 else if (IS_GRID_STEEL(element))
1502 return HitReflectingWalls(element, hit_mask);
1504 else /* IS_GRID_WOOD */
1506 return HitAbsorbingWalls(element, hit_mask);
1512 boolean HitBlock(int element, int hit_mask)
1514 boolean check = FALSE;
1516 if ((element == EL_GATE_STONE || element == EL_GATE_WOOD) &&
1517 game_mm.num_keys == 0)
1520 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1523 int ex = ELX * TILEX + 14;
1524 int ey = ELY * TILEY + 14;
1528 for (i = 1; i < 32; i++)
1533 if ((x == ex || x == ex + 1) && (y == ey || y == ey + 1))
1538 if (check && (element == EL_BLOCK_WOOD || element == EL_GATE_WOOD))
1539 return HitAbsorbingWalls(element, hit_mask);
1543 AddLaserEdge(LX - XS, LY - YS);
1544 AddDamagedField(ELX, ELY);
1547 Box[ELX][ELY] = laser.num_edges;
1549 return HitReflectingWalls(element, hit_mask);
1552 if (element == EL_GATE_STONE || element == EL_GATE_WOOD)
1554 int xs = XS / 2, ys = YS / 2;
1555 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1556 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1558 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1559 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1561 laser.overloaded = (element == EL_GATE_STONE);
1566 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1567 (hit_mask == HIT_MASK_TOP ||
1568 hit_mask == HIT_MASK_LEFT ||
1569 hit_mask == HIT_MASK_RIGHT ||
1570 hit_mask == HIT_MASK_BOTTOM))
1571 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1572 hit_mask == HIT_MASK_BOTTOM),
1573 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1574 hit_mask == HIT_MASK_RIGHT));
1575 AddLaserEdge(LX, LY);
1581 if (element == EL_GATE_STONE && Box[ELX][ELY])
1583 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
1595 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1597 int xs = XS / 2, ys = YS / 2;
1598 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1599 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1601 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1602 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1604 laser.overloaded = (element == EL_BLOCK_STONE);
1609 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1610 (hit_mask == HIT_MASK_TOP ||
1611 hit_mask == HIT_MASK_LEFT ||
1612 hit_mask == HIT_MASK_RIGHT ||
1613 hit_mask == HIT_MASK_BOTTOM))
1614 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1615 hit_mask == HIT_MASK_BOTTOM),
1616 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1617 hit_mask == HIT_MASK_RIGHT));
1618 AddDamagedField(ELX, ELY);
1620 LX = ELX * TILEX + 14;
1621 LY = ELY * TILEY + 14;
1623 AddLaserEdge(LX, LY);
1625 laser.stops_inside_element = TRUE;
1633 boolean HitLaserSource(int element, int hit_mask)
1635 if (HitOnlyAnEdge(element, hit_mask))
1638 PlaySoundStereo(SND_AUTSCH, ST(ELX));
1639 laser.overloaded = TRUE;
1644 boolean HitLaserDestination(int element, int hit_mask)
1646 if (HitOnlyAnEdge(element, hit_mask))
1649 if (element != EL_EXIT_OPEN &&
1650 !(IS_RECEIVER(element) &&
1651 game_mm.kettles_still_needed == 0 &&
1652 laser.current_angle == get_opposite_angle(get_element_angle(element))))
1654 PlaySoundStereo(SND_HOLZ, ST(ELX));
1658 if (IS_RECEIVER(element) ||
1659 (IS_22_5_ANGLE(laser.current_angle) &&
1660 (ELX != (LX + 6 * XS) / TILEX ||
1661 ELY != (LY + 6 * YS) / TILEY ||
1670 LX = ELX * TILEX + 14;
1671 LY = ELY * TILEY + 14;
1673 laser.stops_inside_element = TRUE;
1676 AddLaserEdge(LX, LY);
1677 AddDamagedField(ELX, ELY);
1679 if (game_mm.lights_still_needed == 0)
1680 game_mm.level_solved = TRUE;
1685 boolean HitReflectingWalls(int element, int hit_mask)
1687 /* check if laser hits side of a wall with an angle that is not 90° */
1688 if (!IS_90_ANGLE(laser.current_angle) && (hit_mask == HIT_MASK_TOP ||
1689 hit_mask == HIT_MASK_LEFT ||
1690 hit_mask == HIT_MASK_RIGHT ||
1691 hit_mask == HIT_MASK_BOTTOM))
1693 PlaySoundStereo(SND_HUI, ST(ELX));
1698 if (!IS_DF_GRID(element))
1699 AddLaserEdge(LX, LY);
1701 /* check if laser hits wall with an angle of 45° */
1702 if (!IS_22_5_ANGLE(laser.current_angle))
1704 if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1707 laser.current_angle = get_mirrored_angle(laser.current_angle,
1710 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1713 laser.current_angle = get_mirrored_angle(laser.current_angle,
1717 AddLaserEdge(LX, LY);
1719 XS = 2 * Step[laser.current_angle].x;
1720 YS = 2 * Step[laser.current_angle].y;
1724 else if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1726 laser.current_angle = get_mirrored_angle(laser.current_angle,
1731 if (!IS_DF_GRID(element))
1732 AddLaserEdge(LX, LY);
1737 if (!IS_DF_GRID(element))
1738 AddLaserEdge(LX, LY + YS / 2);
1741 if (!IS_DF_GRID(element))
1742 AddLaserEdge(LX, LY);
1745 YS = 2 * Step[laser.current_angle].y;
1749 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1751 laser.current_angle = get_mirrored_angle(laser.current_angle,
1756 if (!IS_DF_GRID(element))
1757 AddLaserEdge(LX, LY);
1762 if (!IS_DF_GRID(element))
1763 AddLaserEdge(LX + XS / 2, LY);
1766 if (!IS_DF_GRID(element))
1767 AddLaserEdge(LX, LY);
1770 XS = 2 * Step[laser.current_angle].x;
1776 /* reflection at the edge of reflecting DF style wall */
1777 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1779 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1780 hit_mask == HIT_MASK_TOPRIGHT) ||
1781 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1782 hit_mask == HIT_MASK_TOPLEFT) ||
1783 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1784 hit_mask == HIT_MASK_BOTTOMLEFT) ||
1785 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1786 hit_mask == HIT_MASK_BOTTOMRIGHT))
1789 (hit_mask == HIT_MASK_TOPRIGHT || hit_mask == HIT_MASK_BOTTOMLEFT ?
1790 ANG_MIRROR_135 : ANG_MIRROR_45);
1792 PlaySoundStereo(SND_HUI, ST(ELX));
1794 AddDamagedField(ELX, ELY);
1795 AddLaserEdge(LX, LY);
1797 laser.current_angle = get_mirrored_angle(laser.current_angle,
1805 AddLaserEdge(LX, LY);
1811 /* reflection inside an edge of reflecting DF style wall */
1812 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1814 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1815 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT)) ||
1816 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1817 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMRIGHT)) ||
1818 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1819 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT)) ||
1820 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1821 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPLEFT)))
1824 (hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT) ||
1825 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT) ?
1826 ANG_MIRROR_135 : ANG_MIRROR_45);
1828 PlaySoundStereo(SND_HUI, ST(ELX));
1831 AddDamagedField(ELX, ELY);
1834 AddLaserEdge(LX - XS, LY - YS);
1835 AddLaserEdge(LX - XS + (ABS(XS) == 4 ? XS/2 : 0),
1836 LY - YS + (ABS(YS) == 4 ? YS/2 : 0));
1838 laser.current_angle = get_mirrored_angle(laser.current_angle,
1846 AddLaserEdge(LX, LY);
1852 /* check if laser hits DF style wall with an angle of 90° */
1853 if (IS_DF_WALL(element) && IS_90_ANGLE(laser.current_angle))
1855 if ((IS_HORIZ_ANGLE(laser.current_angle) &&
1856 (!(hit_mask & HIT_MASK_TOP) || !(hit_mask & HIT_MASK_BOTTOM))) ||
1857 (IS_VERT_ANGLE(laser.current_angle) &&
1858 (!(hit_mask & HIT_MASK_LEFT) || !(hit_mask & HIT_MASK_RIGHT))))
1860 static int last_LX = 0, last_LY = 0, last_hit_mask = 0;
1862 /* laser at last step touched nothing or the same side of the wall */
1863 if (LX != last_LX || LY != last_LY || hit_mask == last_hit_mask)
1865 AddDamagedField(ELX, ELY);
1872 last_hit_mask = hit_mask;
1879 if (!HitOnlyAnEdge(element, hit_mask))
1881 laser.overloaded = TRUE;
1889 boolean HitAbsorbingWalls(int element, int hit_mask)
1891 if (HitOnlyAnEdge(element, hit_mask))
1895 (hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT))
1897 AddLaserEdge(LX - XS, LY - YS);
1904 (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM))
1906 AddLaserEdge(LX - XS, LY - YS);
1912 if (IS_WALL_WOOD(element) ||
1913 IS_DF_WALL_WOOD(element) ||
1914 IS_GRID_WOOD(element) ||
1915 IS_GRID_WOOD_FIXED(element) ||
1916 IS_GRID_WOOD_AUTO(element) ||
1917 element == EL_FUSE_ON ||
1918 element == EL_BLOCK_WOOD ||
1919 element == EL_GATE_WOOD)
1921 PlaySoundStereo(SND_HOLZ, ST(ELX));
1926 if (IS_WALL_ICE(element))
1930 mask = (LX + XS) / MINI_TILEX - ELX * 2 + 1; /* Quadrant (horizontal) */
1931 mask <<= (((LY + YS) / MINI_TILEY - ELY * 2) > 0) * 2; /* || (vertical) */
1933 /* check if laser hits wall with an angle of 90° */
1934 if (IS_90_ANGLE(laser.current_angle))
1935 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1937 if (mask == 1 || mask == 2 || mask == 4 || mask == 8)
1941 for (i = 0; i < 4; i++)
1943 if (mask == (1 << i) && (XS > 0) == (i % 2) && (YS > 0) == (i / 2))
1944 mask = 15 - (8 >> i);
1945 else if (ABS(XS) == 4 &&
1947 (XS > 0) == (i % 2) &&
1948 (YS < 0) == (i / 2))
1949 mask = 3 + (i / 2) * 9;
1950 else if (ABS(YS) == 4 &&
1952 (XS < 0) == (i % 2) &&
1953 (YS > 0) == (i / 2))
1954 mask = 5 + (i % 2) * 5;
1958 laser.wall_mask = mask;
1960 else if (IS_WALL_AMOEBA(element))
1962 int elx = (LX - 2 * XS) / TILEX;
1963 int ely = (LY - 2 * YS) / TILEY;
1964 int element2 = Feld[elx][ely];
1967 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element2))
1969 laser.dest_element = EL_EMPTY;
1977 mask = (LX - 2 * XS) / 16 - ELX * 2 + 1;
1978 mask <<= ((LY - 2 * YS) / 16 - ELY * 2) * 2;
1980 if (IS_90_ANGLE(laser.current_angle))
1981 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1983 laser.dest_element = element2 | EL_WALL_AMOEBA;
1985 laser.wall_mask = mask;
1991 void OpenExit(int x, int y)
1995 if (!MovDelay[x][y]) /* next animation frame */
1996 MovDelay[x][y] = 4 * delay;
1998 if (MovDelay[x][y]) /* wait some time before next frame */
2003 phase = MovDelay[x][y] / delay;
2005 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2006 DrawGraphicAnimation_MM(x, y, IMG_MM_EXIT_OPENING, 3 - phase);
2008 if (!MovDelay[x][y])
2010 Feld[x][y] = EL_EXIT_OPEN;
2016 void OpenSurpriseBall(int x, int y)
2020 if (!MovDelay[x][y]) /* next animation frame */
2021 MovDelay[x][y] = 50 * delay;
2023 if (MovDelay[x][y]) /* wait some time before next frame */
2027 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2030 int graphic = el2gfx(Store[x][y]);
2032 int dx = RND(26), dy = RND(26);
2034 getGraphicSource(graphic, 0, &bitmap, &gx, &gy);
2036 BlitBitmap(bitmap, drawto, gx + dx, gy + dy, 6, 6,
2037 SX + x * TILEX + dx, SY + y * TILEY + dy);
2039 MarkTileDirty(x, y);
2042 if (!MovDelay[x][y])
2044 Feld[x][y] = Store[x][y];
2053 void MeltIce(int x, int y)
2058 if (!MovDelay[x][y]) /* next animation frame */
2059 MovDelay[x][y] = frames * delay;
2061 if (MovDelay[x][y]) /* wait some time before next frame */
2064 int wall_mask = Store2[x][y];
2065 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_ICE;
2068 phase = frames - MovDelay[x][y] / delay - 1;
2070 if (!MovDelay[x][y])
2074 Feld[x][y] = real_element & (wall_mask ^ 0xFF);
2075 Store[x][y] = Store2[x][y] = 0;
2077 DrawWalls_MM(x, y, Feld[x][y]);
2079 if (Feld[x][y] == EL_WALL_ICE)
2080 Feld[x][y] = EL_EMPTY;
2082 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
2083 if (laser.damage[i].is_mirror)
2087 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
2089 DrawLaser(0, DL_LASER_DISABLED);
2093 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2095 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2097 laser.redraw = TRUE;
2102 void GrowAmoeba(int x, int y)
2107 if (!MovDelay[x][y]) /* next animation frame */
2108 MovDelay[x][y] = frames * delay;
2110 if (MovDelay[x][y]) /* wait some time before next frame */
2113 int wall_mask = Store2[x][y];
2114 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_AMOEBA;
2117 phase = MovDelay[x][y] / delay;
2119 if (!MovDelay[x][y])
2121 Feld[x][y] = real_element;
2122 Store[x][y] = Store2[x][y] = 0;
2124 DrawWalls_MM(x, y, Feld[x][y]);
2125 DrawLaser(0, DL_LASER_ENABLED);
2127 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2129 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2134 static void Explode_MM(int x, int y, int phase, int mode)
2136 int num_phase = 9, delay = 2;
2137 int last_phase = num_phase * delay;
2138 int half_phase = (num_phase / 2) * delay;
2140 laser.redraw = TRUE;
2142 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2144 int center_element = Feld[x][y];
2146 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2148 /* put moving element to center field (and let it explode there) */
2149 center_element = MovingOrBlocked2Element_MM(x, y);
2150 RemoveMovingField_MM(x, y);
2152 Feld[x][y] = center_element;
2155 if (center_element == EL_BOMB || IS_MCDUFFIN(center_element))
2156 Store[x][y] = center_element;
2158 Store[x][y] = EL_EMPTY;
2160 Store2[x][y] = mode;
2161 Feld[x][y] = EL_EXPLODING_OPAQUE;
2162 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2168 Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
2170 if (phase == half_phase)
2172 Feld[x][y] = EL_EXPLODING_TRANSP;
2174 if (x == ELX && y == ELY)
2178 if (phase == last_phase)
2180 if (Store[x][y] == EL_BOMB)
2182 laser.num_damages--;
2183 DrawLaser(0, DL_LASER_DISABLED);
2184 laser.num_edges = 0;
2186 Bang_MM(laser.start_edge.x, laser.start_edge.y);
2187 Store[x][y] = EL_EMPTY;
2189 else if (IS_MCDUFFIN(Store[x][y]))
2191 game_mm.game_over = TRUE;
2192 game_mm.game_over_cause = GAME_OVER_BOMB;
2193 Store[x][y] = EL_EMPTY;
2196 Feld[x][y] = Store[x][y];
2197 Store[x][y] = Store2[x][y] = 0;
2198 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2200 InitField(x, y, FALSE);
2203 else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2205 int graphic = IMG_MM_DEFAULT_EXPLODING;
2206 int graphic_phase = (phase / delay - 1);
2210 if (Store2[x][y] == EX_KETTLE)
2212 if (graphic_phase < 3)
2214 graphic = IMG_MM_KETTLE_EXPLODING;
2216 else if (graphic_phase < 5)
2222 graphic = IMG_EMPTY;
2226 else if (Store2[x][y] == EX_SHORT)
2228 if (graphic_phase < 4)
2234 graphic = IMG_EMPTY;
2239 getGraphicSource(graphic, graphic_phase, &bitmap, &src_x, &src_y);
2241 BlitBitmap(bitmap, drawto_field, src_x, src_y, TILEX, TILEY,
2242 FX + x * TILEX, FY + y * TILEY);
2244 MarkTileDirty(x, y);
2248 static void Bang_MM(int x, int y)
2250 int element = Feld[x][y];
2251 int mode = EX_NORMAL;
2254 DrawLaser(0, DL_LASER_ENABLED);
2273 if (IS_PACMAN(element))
2274 PlaySoundStereo(SND_QUIEK, ST(x));
2275 else if (element == EL_BOMB || IS_MCDUFFIN(element))
2276 PlaySoundStereo(SND_ROAAAR, ST(x));
2277 else if (element == EL_KEY)
2278 PlaySoundStereo(SND_KLING, ST(x));
2280 PlaySoundStereo((mode == EX_SHORT ? SND_WHOOSH : SND_KABUMM), ST(x));
2282 Explode_MM(x, y, EX_PHASE_START, mode);
2285 void TurnRound(int x, int y)
2297 { 0, 0 }, { 0, 0 }, { 0, 0 },
2302 int left, right, back;
2306 { MV_DOWN, MV_UP, MV_RIGHT },
2307 { MV_UP, MV_DOWN, MV_LEFT },
2309 { MV_LEFT, MV_RIGHT, MV_DOWN },
2310 { 0,0,0 }, { 0,0,0 }, { 0,0,0 },
2311 { MV_RIGHT, MV_LEFT, MV_UP }
2314 int element = Feld[x][y];
2315 int old_move_dir = MovDir[x][y];
2316 int right_dir = turn[old_move_dir].right;
2317 int back_dir = turn[old_move_dir].back;
2318 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
2319 int right_x = x + right_dx, right_y = y + right_dy;
2321 if (element == EL_PACMAN)
2323 boolean can_turn_right = FALSE;
2325 if (IN_LEV_FIELD(right_x, right_y) &&
2326 IS_EATABLE4PACMAN(Feld[right_x][right_y]))
2327 can_turn_right = TRUE;
2330 MovDir[x][y] = right_dir;
2332 MovDir[x][y] = back_dir;
2338 static void StartMoving_MM(int x, int y)
2340 int element = Feld[x][y];
2345 if (CAN_MOVE(element))
2349 if (MovDelay[x][y]) /* wait some time before next movement */
2357 /* now make next step */
2359 Moving2Blocked_MM(x, y, &newx, &newy); /* get next screen position */
2361 if (element == EL_PACMAN &&
2362 IN_LEV_FIELD(newx, newy) && IS_EATABLE4PACMAN(Feld[newx][newy]) &&
2363 !ObjHit(newx, newy, HIT_POS_CENTER))
2365 Store[newx][newy] = Feld[newx][newy];
2366 Feld[newx][newy] = EL_EMPTY;
2368 DrawField_MM(newx, newy);
2370 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy) ||
2371 ObjHit(newx, newy, HIT_POS_CENTER))
2373 /* object was running against a wall */
2380 InitMovingField_MM(x, y, MovDir[x][y]);
2384 ContinueMoving_MM(x, y);
2387 static void ContinueMoving_MM(int x, int y)
2389 int element = Feld[x][y];
2390 int direction = MovDir[x][y];
2391 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2392 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2393 int horiz_move = (dx!=0);
2394 int newx = x + dx, newy = y + dy;
2395 int step = (horiz_move ? dx : dy) * TILEX / 8;
2397 MovPos[x][y] += step;
2399 if (ABS(MovPos[x][y]) >= TILEX) /* object reached its destination */
2401 Feld[x][y] = EL_EMPTY;
2402 Feld[newx][newy] = element;
2404 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2405 MovDelay[newx][newy] = 0;
2407 if (!CAN_MOVE(element))
2408 MovDir[newx][newy] = 0;
2411 DrawField_MM(newx, newy);
2413 Stop[newx][newy] = TRUE;
2415 if (element == EL_PACMAN)
2417 if (Store[newx][newy] == EL_BOMB)
2418 Bang_MM(newx, newy);
2420 if (IS_WALL_AMOEBA(Store[newx][newy]) &&
2421 (LX + 2 * XS) / TILEX == newx &&
2422 (LY + 2 * YS) / TILEY == newy)
2429 else /* still moving on */
2434 laser.redraw = TRUE;
2437 void ClickElement(int mx, int my, int button)
2439 static unsigned int click_delay = 0;
2440 static int click_delay_value = CLICK_DELAY_SHORT;
2441 static boolean new_button = TRUE;
2443 int x = (mx - SX) / TILEX, y = (my - SY) / TILEY;
2445 /* do not rotate objects hit by the laser after the game was solved */
2446 if (game_mm.level_solved && Hit[x][y])
2449 if (button == MB_RELEASED)
2452 click_delay_value = CLICK_DELAY_SHORT;
2454 /* release eventually hold auto-rotating mirror */
2455 RotateMirror(x, y, MB_RELEASED);
2460 if (!DelayReached(&click_delay, click_delay_value) && !new_button)
2463 if (button == MB_MIDDLEBUTTON) /* middle button has no function */
2466 if (!IN_PIX_FIELD(mx - SX, my - SY))
2469 if (Feld[x][y] == EL_EMPTY)
2472 element = Feld[x][y];
2474 if (IS_MIRROR(element) ||
2475 IS_BEAMER(element) ||
2476 IS_POLAR(element) ||
2477 IS_POLAR_CROSS(element) ||
2478 IS_DF_MIRROR(element) ||
2479 IS_DF_MIRROR_AUTO(element))
2481 RotateMirror(x, y, button);
2483 else if (IS_MCDUFFIN(element))
2485 if (!laser.fuse_off)
2487 DrawLaser(0, DL_LASER_DISABLED);
2494 element = get_rotated_element(element, BUTTON_ROTATION(button));
2495 laser.start_angle = get_element_angle(element);
2499 Feld[x][y] = element;
2506 if (!laser.fuse_off)
2509 else if (element == EL_FUSE_ON && laser.fuse_off)
2511 if (x != laser.fuse_x || y != laser.fuse_y)
2514 laser.fuse_off = FALSE;
2515 laser.fuse_x = laser.fuse_y = -1;
2517 DrawGraphic_MM(x, y, IMG_MM_FUSE_ACTIVE);
2520 else if (element == EL_FUSE_ON && !laser.fuse_off && new_button)
2522 laser.fuse_off = TRUE;
2525 laser.overloaded = FALSE;
2527 DrawLaser(0, DL_LASER_DISABLED);
2528 DrawGraphic_MM(x, y, IMG_MM_FUSE);
2530 else if (element == EL_LIGHTBALL)
2534 DrawLaser(0, DL_LASER_ENABLED);
2537 click_delay_value = (new_button ? CLICK_DELAY_LONG : CLICK_DELAY_SHORT);
2541 void RotateMirror(int x, int y, int button)
2543 static int hold_x = -1, hold_y = -1;
2545 if (button == MB_RELEASED)
2547 /* release eventually hold auto-rotating mirror */
2554 if (IS_MIRROR(Feld[x][y]) ||
2555 IS_POLAR_CROSS(Feld[x][y]) ||
2556 IS_POLAR(Feld[x][y]) ||
2557 IS_BEAMER(Feld[x][y]) ||
2558 IS_DF_MIRROR(Feld[x][y]) ||
2559 IS_GRID_STEEL_AUTO(Feld[x][y]) ||
2560 IS_GRID_WOOD_AUTO(Feld[x][y]))
2562 Feld[x][y] = get_rotated_element(Feld[x][y], BUTTON_ROTATION(button));
2564 else if (IS_DF_MIRROR_AUTO(Feld[x][y]))
2566 if (button == MB_LEFTBUTTON)
2568 /* left mouse button only for manual adjustment, no auto-rotating;
2569 freeze mirror for until mouse button released */
2573 else if (button == MB_RIGHTBUTTON && (hold_x != x || hold_y != y))
2575 Feld[x][y] = get_rotated_element(Feld[x][y], ROTATE_RIGHT);
2579 if (IS_GRID_STEEL_AUTO(Feld[x][y]) || IS_GRID_WOOD_AUTO(Feld[x][y]))
2581 int edge = Hit[x][y];
2587 DrawLaser(edge - 1, DL_LASER_DISABLED);
2591 else if (ObjHit(x, y, HIT_POS_CENTER))
2593 int edge = Hit[x][y];
2597 Error(ERR_WARN, "RotateMirror: inconsistent field Hit[][]!\n");
2601 DrawLaser(edge - 1, DL_LASER_DISABLED);
2608 if (ObjHit(x, y, HIT_POS_EDGE | HIT_POS_BETWEEN))
2613 if ((IS_BEAMER(Feld[x][y]) ||
2614 IS_POLAR(Feld[x][y]) ||
2615 IS_POLAR_CROSS(Feld[x][y])) && x == ELX && y == ELY)
2619 if (IS_BEAMER(Feld[x][y]))
2622 printf("TEST (%d, %d) [%d] [%d]\n",
2624 laser.beamer_edge, laser.beamer[1].num);
2634 DrawLaser(0, DL_LASER_ENABLED);
2638 void AutoRotateMirrors()
2640 static unsigned int rotate_delay = 0;
2643 if (!DelayReached(&rotate_delay, AUTO_ROTATE_DELAY))
2646 for (x = 0; x < lev_fieldx; x++)
2648 for (y = 0; y < lev_fieldy; y++)
2650 int element = Feld[x][y];
2652 /* do not rotate objects hit by the laser after the game was solved */
2653 if (game_mm.level_solved && Hit[x][y])
2656 if (IS_DF_MIRROR_AUTO(element) ||
2657 IS_GRID_WOOD_AUTO(element) ||
2658 IS_GRID_STEEL_AUTO(element) ||
2659 element == EL_REFRACTOR)
2660 RotateMirror(x, y, MB_RIGHTBUTTON);
2665 boolean ObjHit(int obx, int oby, int bits)
2672 if (bits & HIT_POS_CENTER)
2674 if (ReadPixel(drawto, SX + obx + 15, SY + oby + 15) == pen_ray)
2678 if (bits & HIT_POS_EDGE)
2680 for (i = 0; i < 4; i++)
2681 if (ReadPixel(drawto,
2682 SX + obx + 31 * (i % 2),
2683 SY + oby + 31 * (i / 2)) == pen_ray)
2687 if (bits & HIT_POS_BETWEEN)
2689 for (i = 0; i < 4; i++)
2690 if (ReadPixel(drawto,
2691 SX + 4 + obx + 22 * (i % 2),
2692 SY + 4 + oby + 22 * (i / 2)) == pen_ray)
2699 void DeletePacMan(int px, int py)
2705 if (game_mm.num_pacman <= 1)
2707 game_mm.num_pacman = 0;
2711 for (i = 0; i < game_mm.num_pacman; i++)
2712 if (game_mm.pacman[i].x == px && game_mm.pacman[i].y == py)
2715 game_mm.num_pacman--;
2717 for (j = i; j < game_mm.num_pacman; j++)
2719 game_mm.pacman[j].x = game_mm.pacman[j + 1].x;
2720 game_mm.pacman[j].y = game_mm.pacman[j + 1].y;
2721 game_mm.pacman[j].dir = game_mm.pacman[j + 1].dir;
2725 void ColorCycling(void)
2727 static int CC, Cc = 0;
2729 static int color, old = 0xF00, new = 0x010, mult = 1;
2730 static unsigned short red, green, blue;
2732 if (color_status == STATIC_COLORS)
2737 if (CC < Cc || CC > Cc + 50)
2741 color = old + new * mult;
2747 if (ABS(mult) == 16)
2757 red = 0x0e00 * ((color & 0xF00) >> 8);
2758 green = 0x0e00 * ((color & 0x0F0) >> 4);
2759 blue = 0x0e00 * ((color & 0x00F));
2760 SetRGB(pen_magicolor[0], red, green, blue);
2762 red = 0x1111 * ((color & 0xF00) >> 8);
2763 green = 0x1111 * ((color & 0x0F0) >> 4);
2764 blue = 0x1111 * ((color & 0x00F));
2765 SetRGB(pen_magicolor[1], red, green, blue);
2769 static void GameActions_MM_Ext(byte action[MAX_PLAYERS], boolean warp_mode)
2771 static unsigned int action_delay = 0;
2772 static unsigned int pacman_delay = 0;
2773 static unsigned int energy_delay = 0;
2774 static unsigned int overload_delay = 0;
2780 WaitUntilDelayReached(&action_delay, GameFrameDelay);
2782 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2785 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2787 element = Feld[x][y];
2789 if (!IS_MOVING(x, y) && CAN_MOVE(element))
2790 StartMoving_MM(x, y);
2791 else if (IS_MOVING(x, y))
2792 ContinueMoving_MM(x, y);
2793 else if (IS_EXPLODING(element))
2794 Explode_MM(x, y, Frame[x][y], EX_NORMAL);
2795 else if (element == EL_EXIT_OPENING)
2797 else if (element == EL_GRAY_BALL_OPENING)
2798 OpenSurpriseBall(x, y);
2799 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_ICE)
2801 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_AMOEBA)
2805 AutoRotateMirrors();
2808 /* !!! CHANGE THIS: REDRAW ONLY WHEN NEEDED !!! */
2810 /* redraw after Explode_MM() ... */
2812 DrawLaser(0, DL_LASER_ENABLED);
2813 laser.redraw = FALSE;
2818 if (game_mm.num_pacman && DelayReached(&pacman_delay, 250))
2822 if (laser.num_damages > MAX_LASER_LEN && !laser.fuse_off)
2824 DrawLaser(0, DL_LASER_DISABLED);
2829 if (DelayReached(&energy_delay, 4000))
2831 game_mm.energy_left--;
2832 if (game_mm.energy_left >= 0)
2835 BlitBitmap(pix[PIX_DOOR], drawto,
2836 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
2837 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
2838 DX_ENERGY, DY_ENERGY);
2840 redraw_mask |= REDRAW_DOOR_1;
2842 else if (setup.time_limit)
2846 for (i = 15; i >= 0; i--)
2849 SetRGB(pen_ray, 0x0000, 0x0000, i * color_scale);
2851 pen_ray = GetPixelFromRGB(window,
2852 native_mm_level.laser_red * 0x11 * i,
2853 native_mm_level.laser_green * 0x11 * i,
2854 native_mm_level.laser_blue * 0x11 * i);
2856 DrawLaser(0, DL_LASER_ENABLED);
2861 StopSound(SND_WARNTON);
2864 DrawLaser(0, DL_LASER_DISABLED);
2865 game_mm.game_over = TRUE;
2866 game_mm.game_over_cause = GAME_OVER_NO_ENERGY;
2869 if (Request("Out of magic energy ! Play it again ?",
2870 REQ_ASK | REQ_STAY_CLOSED))
2876 game_status = MAINMENU;
2885 element = laser.dest_element;
2888 if (element != Feld[ELX][ELY])
2890 printf("element == %d, Feld[ELX][ELY] == %d\n",
2891 element, Feld[ELX][ELY]);
2895 if (!laser.overloaded && laser.overload_value == 0 &&
2896 element != EL_BOMB &&
2897 element != EL_MINE &&
2898 element != EL_BALL_GRAY &&
2899 element != EL_BLOCK_STONE &&
2900 element != EL_BLOCK_WOOD &&
2901 element != EL_FUSE_ON &&
2902 element != EL_FUEL_FULL &&
2903 !IS_WALL_ICE(element) &&
2904 !IS_WALL_AMOEBA(element))
2907 if (((laser.overloaded && laser.overload_value < MAX_LASER_OVERLOAD) ||
2908 (!laser.overloaded && laser.overload_value > 0)) &&
2909 DelayReached(&overload_delay, 60 + !laser.overloaded * 120))
2911 if (laser.overloaded)
2912 laser.overload_value++;
2914 laser.overload_value--;
2916 if (game_mm.cheat_no_overload)
2918 laser.overloaded = FALSE;
2919 laser.overload_value = 0;
2922 game_mm.laser_overload_value = laser.overload_value;
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]);