1 // ============================================================================
2 // Mirror Magic -- McDuffin's Revenge
3 // ----------------------------------------------------------------------------
4 // (c) 1994-2017 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
18 /* graphic position values for game controls */
19 #define ENERGY_XSIZE 32
20 #define ENERGY_YSIZE MAX_LASER_ENERGY
21 #define OVERLOAD_XSIZE ENERGY_XSIZE
22 #define OVERLOAD_YSIZE MAX_LASER_OVERLOAD
24 /* values for Explode_MM() */
25 #define EX_PHASE_START 0
30 /* special positions in the game control window (relative to control window) */
39 #define XX_OVERLOAD 60
40 #define YY_OVERLOAD YY_ENERGY
42 /* special positions in the game control window (relative to main window) */
43 #define DX_LEVEL (DX + XX_LEVEL)
44 #define DY_LEVEL (DY + YY_LEVEL)
45 #define DX_KETTLES (DX + XX_KETTLES)
46 #define DY_KETTLES (DY + YY_KETTLES)
47 #define DX_SCORE (DX + XX_SCORE)
48 #define DY_SCORE (DY + YY_SCORE)
49 #define DX_ENERGY (DX + XX_ENERGY)
50 #define DY_ENERGY (DY + YY_ENERGY)
51 #define DX_OVERLOAD (DX + XX_OVERLOAD)
52 #define DY_OVERLOAD (DY + YY_OVERLOAD)
54 #define IS_LOOP_SOUND(s) ((s) == SND_FUEL)
55 #define IS_MUSIC_SOUND(s) ((s) == SND_TYGER || (s) == SND_VOYAGER)
57 /* game button identifiers */
58 #define GAME_CTRL_ID_LEFT 0
59 #define GAME_CTRL_ID_MIDDLE 1
60 #define GAME_CTRL_ID_RIGHT 2
62 #define NUM_GAME_BUTTONS 3
64 /* values for DrawLaser() */
65 #define DL_LASER_DISABLED 0
66 #define DL_LASER_ENABLED 1
68 /* values for 'click_delay_value' in ClickElement() */
69 #define CLICK_DELAY_SHORT 125
70 #define CLICK_DELAY_LONG 250
72 #define AUTO_ROTATE_DELAY CLICK_DELAY_SHORT
73 #define INIT_GAME_ACTIONS_DELAY ONE_SECOND_DELAY
74 #define NUM_INIT_CYCLE_STEPS 16
76 /* forward declaration for internal use */
77 static int MovingOrBlocked2Element_MM(int, int);
78 static void Bang_MM(int, int);
79 static void RaiseScore_MM(int);
80 static void RemoveMovingField_MM(int, int);
81 static void InitMovingField_MM(int, int, int);
82 static void ContinueMoving_MM(int, int);
83 static void Moving2Blocked_MM(int, int, int *, int *);
86 static int get_element_angle(int element)
88 int element_phase = get_element_phase(element);
90 if (IS_MIRROR_FIXED(element) ||
91 IS_MCDUFFIN(element) ||
94 return 4 * element_phase;
99 static int get_opposite_angle(int angle)
101 int opposite_angle = angle + ANG_RAY_180;
103 /* make sure "opposite_angle" is in valid interval [0, 15] */
104 return (opposite_angle + 16) % 16;
107 static int get_mirrored_angle(int laser_angle, int mirror_angle)
109 int reflected_angle = 16 - laser_angle + mirror_angle;
111 /* make sure "reflected_angle" is in valid interval [0, 15] */
112 return (reflected_angle + 16) % 16;
115 static void InitMovDir_MM(int x, int y)
117 int element = Feld[x][y];
118 static int direction[3][4] =
120 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
121 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
122 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
127 case EL_PACMAN_RIGHT:
131 Feld[x][y] = EL_PACMAN;
132 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
140 static void InitField(int x, int y, boolean init_game)
142 int element = Feld[x][y];
147 Feld[x][y] = EL_EMPTY;
152 if (native_mm_level.auto_count_kettles)
153 game_mm.kettles_still_needed++;
156 case EL_LIGHTBULB_OFF:
157 game_mm.lights_still_needed++;
161 if (IS_MIRROR(element) ||
162 IS_BEAMER_OLD(element) ||
163 IS_BEAMER(element) ||
165 IS_POLAR_CROSS(element) ||
166 IS_DF_MIRROR(element) ||
167 IS_DF_MIRROR_AUTO(element) ||
168 IS_GRID_STEEL_AUTO(element) ||
169 IS_GRID_WOOD_AUTO(element) ||
170 IS_FIBRE_OPTIC(element))
172 if (IS_BEAMER_OLD(element))
174 Feld[x][y] = EL_BEAMER_BLUE_START + (element - EL_BEAMER_START);
175 element = Feld[x][y];
178 if (!IS_FIBRE_OPTIC(element))
180 static int steps_grid_auto = 0;
182 if (game_mm.num_cycle == 0) /* initialize cycle steps for grids */
183 steps_grid_auto = RND(16) * (RND(2) ? -1 : +1);
185 if (IS_GRID_STEEL_AUTO(element) ||
186 IS_GRID_WOOD_AUTO(element))
187 game_mm.cycle[game_mm.num_cycle].steps = steps_grid_auto;
189 game_mm.cycle[game_mm.num_cycle].steps = RND(16) * (RND(2) ? -1 : +1);
191 game_mm.cycle[game_mm.num_cycle].x = x;
192 game_mm.cycle[game_mm.num_cycle].y = y;
196 if (IS_BEAMER(element) || IS_FIBRE_OPTIC(element))
198 int beamer_nr = BEAMER_NR(element);
199 int nr = laser.beamer[beamer_nr][0].num;
201 laser.beamer[beamer_nr][nr].x = x;
202 laser.beamer[beamer_nr][nr].y = y;
203 laser.beamer[beamer_nr][nr].num = 1;
206 else if (IS_PACMAN(element))
210 else if (IS_MCDUFFIN(element) || IS_LASER(element))
212 laser.start_edge.x = x;
213 laser.start_edge.y = y;
214 laser.start_angle = get_element_angle(element);
221 static void InitCycleElements_RotateSingleStep()
225 if (game_mm.num_cycle == 0) /* no elements to cycle */
228 for (i = 0; i < game_mm.num_cycle; i++)
230 int x = game_mm.cycle[i].x;
231 int y = game_mm.cycle[i].y;
232 int step = SIGN(game_mm.cycle[i].steps);
233 int last_element = Feld[x][y];
234 int next_element = get_rotated_element(last_element, step);
236 if (!game_mm.cycle[i].steps)
239 Feld[x][y] = next_element;
242 game_mm.cycle[i].steps -= step;
246 static void InitLaser()
248 int start_element = Feld[laser.start_edge.x][laser.start_edge.y];
249 int step = (IS_LASER(start_element) ? 4 : 0);
251 LX = laser.start_edge.x * TILEX;
252 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
255 LX += (laser.start_angle == ANG_RAY_RIGHT ? 28 + step : 0 - step);
257 LY = laser.start_edge.y * TILEY;
258 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
259 LY += (laser.start_angle == ANG_RAY_DOWN ? 28 + step : 0 - step);
263 XS = 2 * Step[laser.start_angle].x;
264 YS = 2 * Step[laser.start_angle].y;
266 laser.current_angle = laser.start_angle;
268 laser.num_damages = 0;
270 laser.num_beamers = 0;
271 laser.beamer_edge[0] = 0;
273 AddLaserEdge(LX, LY); /* set laser starting edge */
275 pen_ray = GetPixelFromRGB(window,
276 native_mm_level.laser_red * 0xFF,
277 native_mm_level.laser_green * 0xFF,
278 native_mm_level.laser_blue * 0xFF);
281 void InitGameEngine_MM()
285 /* set global game control values */
286 game_mm.num_cycle = 0;
287 game_mm.num_pacman = 0;
290 game_mm.energy_left = 0; // later set to "native_mm_level.time"
291 game_mm.kettles_still_needed =
292 (native_mm_level.auto_count_kettles ? 0 : native_mm_level.kettles_needed);
293 game_mm.lights_still_needed = 0;
294 game_mm.num_keys = 0;
296 game_mm.level_solved = FALSE;
297 game_mm.game_over = FALSE;
298 game_mm.game_over_cause = 0;
300 game_mm.laser_overload_value = 0;
302 /* set global laser control values (must be set before "InitLaser()") */
303 laser.start_edge.x = 0;
304 laser.start_edge.y = 0;
305 laser.start_angle = 0;
307 for (i = 0; i < MAX_NUM_BEAMERS; i++)
308 laser.beamer[i][0].num = laser.beamer[i][1].num = 0;
310 laser.overloaded = FALSE;
311 laser.overload_value = 0;
312 laser.fuse_off = FALSE;
313 laser.fuse_x = laser.fuse_y = -1;
315 laser.dest_element = EL_EMPTY;
320 for (x = 0; x < lev_fieldx; x++)
322 for (y = 0; y < lev_fieldy; y++)
324 Feld[x][y] = Ur[x][y];
325 Hit[x][y] = Box[x][y] = 0;
327 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
328 Store[x][y] = Store2[x][y] = 0;
332 InitField(x, y, TRUE);
337 CloseDoor(DOOR_CLOSE_1);
343 void InitGameActions_MM()
345 int num_init_game_frames = INIT_GAME_ACTIONS_DELAY / GAME_FRAME_DELAY;
346 int cycle_steps_done = 0;
352 /* copy default game door content to main double buffer */
353 BlitBitmap(pix[PIX_DOOR], drawto,
354 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
358 DrawText(DX_LEVEL, DY_LEVEL,
359 int2str(level_nr, 2), FONT_TEXT_2);
360 DrawText(DX_KETTLES, DY_KETTLES,
361 int2str(game_mm.kettles_still_needed, 3), FONT_TEXT_2);
362 DrawText(DX_SCORE, DY_SCORE,
363 int2str(game_mm.score, 4), FONT_TEXT_2);
372 /* copy actual game door content to door double buffer for OpenDoor() */
373 BlitBitmap(drawto, pix[PIX_DB_DOOR],
374 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
378 OpenDoor(DOOR_OPEN_ALL);
381 if (setup.sound_loops)
382 PlaySoundExt(SND_FUEL, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT, SND_CTRL_PLAY_LOOP);
384 for (i = 0; i <= num_init_game_frames; i++)
386 if (!setup.sound_loops)
387 PlaySoundStereo(SND_FUEL, SOUND_MAX_RIGHT);
389 game_mm.energy_left = native_mm_level.time * i / num_init_game_frames;
391 UpdateAndDisplayGameControlValues();
393 while (cycle_steps_done < NUM_INIT_CYCLE_STEPS * i / num_init_game_frames)
395 InitCycleElements_RotateSingleStep();
405 if (setup.quick_doors)
410 if (setup.sound_loops)
414 if (setup.sound_music && num_bg_loops)
415 PlayMusic(level_nr % num_bg_loops);
421 void AddLaserEdge(int lx, int ly)
423 if (lx < -2 || ly < -2 || lx >= SXSIZE + 2 || ly >= SYSIZE + 2)
425 Error(ERR_WARN, "AddLaserEdge: out of bounds: %d, %d", lx, ly);
430 laser.edge[laser.num_edges].x = SX + 2 + lx;
431 laser.edge[laser.num_edges].y = SY + 2 + ly;
437 void AddDamagedField(int ex, int ey)
439 laser.damage[laser.num_damages].is_mirror = FALSE;
440 laser.damage[laser.num_damages].angle = laser.current_angle;
441 laser.damage[laser.num_damages].edge = laser.num_edges;
442 laser.damage[laser.num_damages].x = ex;
443 laser.damage[laser.num_damages].y = ey;
453 int last_x = laser.edge[laser.num_edges - 1].x - SX - 2;
454 int last_y = laser.edge[laser.num_edges - 1].y - SY - 2;
456 return ((x - last_x) * XS < 0 || (y - last_y) * YS < 0);
462 static int getMaskFromElement(int element)
464 if (IS_GRID(element))
465 return IMG_MM_MASK_GRID_1 + get_element_phase(element);
466 else if (IS_MCDUFFIN(element))
467 return IMG_MM_MASK_MCDUFFIN_RIGHT + get_element_phase(element);
468 else if (IS_RECTANGLE(element) || IS_DF_GRID(element))
469 return IMG_MM_MASK_RECTANGLE;
471 return IMG_MM_MASK_CIRCLE;
479 printf("ScanPixel: start scanning at (%d, %d) [%d, %d] [%d, %d]\n",
480 LX, LY, LX / TILEX, LY / TILEY, LX % TILEX, LY % TILEY);
483 /* follow laser beam until it hits something (at least the screen border) */
484 while (hit_mask == HIT_MASK_NO_HIT)
490 if (SX + LX < REAL_SX || SX + LX >= REAL_SX + FULL_SXSIZE ||
491 SY + LY < REAL_SY || SY + LY >= REAL_SY + FULL_SYSIZE)
493 printf("ScanPixel: touched screen border!\n");
499 for (i = 0; i < 4; i++)
501 int px = LX + (i % 2) * 2;
502 int py = LY + (i / 2) * 2;
505 int lx = (px + TILEX) / TILEX - 1; /* ...+TILEX...-1 to get correct */
506 int ly = (py + TILEY) / TILEY - 1; /* negative values! */
509 if (IN_LEV_FIELD(lx, ly))
511 int element = Feld[lx][ly];
513 if (element == EL_EMPTY || element == EL_EXPLODING_TRANSP)
517 else if (IS_WALL(element) || IS_WALL_CHANGING(element))
519 int pos = dy / MINI_TILEY * 2 + dx / MINI_TILEX;
521 pixel = ((element & (1 << pos)) ? 1 : 0);
525 int graphic_mask = getMaskFromElement(element);
530 getGraphicSource(graphic_mask, 0, &bitmap, &src_x, &src_y);
535 pixel = (ReadPixel(bitmap, mask_x, mask_y) ? 1 : 0);
540 pixel = (SX + px < REAL_SX || SX + px >= REAL_SX + FULL_SXSIZE ||
541 SY + py < REAL_SY || SY + py >= REAL_SY + FULL_SYSIZE);
544 if ((Sign[laser.current_angle] & (1 << i)) && pixel)
545 hit_mask |= (1 << i);
548 if (hit_mask == HIT_MASK_NO_HIT)
550 /* hit nothing -- go on with another step */
562 int end = 0, rf = laser.num_edges;
564 /* do not scan laser again after the game was lost for whatever reason */
565 if (game_mm.game_over)
568 laser.overloaded = FALSE;
569 laser.stops_inside_element = FALSE;
571 DrawLaser(0, DL_LASER_ENABLED);
574 printf("Start scanning with LX == %d, LY == %d, XS == %d, YS == %d\n",
582 if (laser.num_edges > MAX_LASER_LEN || laser.num_damages > MAX_LASER_LEN)
585 laser.overloaded = TRUE;
590 hit_mask = ScanPixel();
593 printf("Hit something at LX == %d, LY == %d, XS == %d, YS == %d\n",
597 /* hit something -- check out what it was */
598 ELX = (LX + XS) / TILEX;
599 ELY = (LY + YS) / TILEY;
602 printf("hit_mask (1) == '%x' (%d, %d) (%d, %d)\n",
603 hit_mask, LX, LY, ELX, ELY);
606 if (!IN_LEV_FIELD(ELX, ELY) || !IN_PIX_FIELD(LX, LY))
609 laser.dest_element = element;
614 if (hit_mask == (HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT))
616 /* we have hit the top-right and bottom-left element --
617 choose the bottom-left one */
618 /* !!! THIS CAN BE DONE MORE INTELLIGENTLY, FOR EXAMPLE, IF ONE
619 ELEMENT WAS STEEL AND THE OTHER ONE WAS ICE => ALWAYS CHOOSE
620 THE ICE AND MELT IT AWAY INSTEAD OF OVERLOADING LASER !!! */
621 ELX = (LX - 2) / TILEX;
622 ELY = (LY + 2) / TILEY;
625 if (hit_mask == (HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT))
627 /* we have hit the top-left and bottom-right element --
628 choose the top-left one */
629 /* !!! SEE ABOVE !!! */
630 ELX = (LX - 2) / TILEX;
631 ELY = (LY - 2) / TILEY;
635 printf("hit_mask (2) == '%x' (%d, %d) (%d, %d)\n",
636 hit_mask, LX, LY, ELX, ELY);
639 element = Feld[ELX][ELY];
640 laser.dest_element = element;
643 printf("Hit element %d at (%d, %d) [%d, %d] [%d, %d] [%d]\n",
646 LX % TILEX, LY % TILEY,
651 if (!IN_LEV_FIELD(ELX, ELY))
652 printf("WARNING! (1) %d, %d (%d)\n", ELX, ELY, element);
655 if (element == EL_EMPTY)
657 if (!HitOnlyAnEdge(element, hit_mask))
660 else if (element == EL_FUSE_ON)
662 if (HitPolarizer(element, hit_mask))
665 else if (IS_GRID(element) || IS_DF_GRID(element))
667 if (HitPolarizer(element, hit_mask))
670 else if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD ||
671 element == EL_GATE_STONE || element == EL_GATE_WOOD)
673 if (HitBlock(element, hit_mask))
680 else if (IS_MCDUFFIN(element))
682 if (HitLaserSource(element, hit_mask))
685 else if ((element >= EL_EXIT_CLOSED && element <= EL_EXIT_OPEN) ||
686 IS_RECEIVER(element))
688 if (HitLaserDestination(element, hit_mask))
691 else if (IS_WALL(element))
693 if (IS_WALL_STEEL(element) || IS_DF_WALL_STEEL(element))
695 if (HitReflectingWalls(element, hit_mask))
700 if (HitAbsorbingWalls(element, hit_mask))
706 if (HitElement(element, hit_mask))
711 DrawLaser(rf - 1, DL_LASER_ENABLED);
712 rf = laser.num_edges;
716 if (laser.dest_element != Feld[ELX][ELY])
718 printf("ALARM: laser.dest_element == %d, Feld[ELX][ELY] == %d\n",
719 laser.dest_element, Feld[ELX][ELY]);
723 if (!end && !laser.stops_inside_element && !StepBehind())
726 printf("ScanLaser: Go one step back\n");
732 AddLaserEdge(LX, LY);
736 DrawLaser(rf - 1, DL_LASER_ENABLED);
741 if (!IN_LEV_FIELD(ELX, ELY))
742 printf("WARNING! (2) %d, %d\n", ELX, ELY);
746 void DrawLaserExt(int start_edge, int num_edges, int mode)
752 printf("DrawLaserExt: start_edge, num_edges, mode == %d, %d, %d\n",
753 start_edge, num_edges, mode);
758 Error(ERR_WARN, "DrawLaserExt: start_edge < 0");
765 Error(ERR_WARN, "DrawLaserExt: num_edges < 0");
771 if (mode == DL_LASER_DISABLED)
773 printf("DrawLaser: Delete laser from edge %d\n", start_edge);
777 /* now draw the laser to the backbuffer and (if enabled) to the screen */
778 DrawLines(drawto, &laser.edge[start_edge], num_edges,
779 (mode == DL_LASER_ENABLED ? pen_ray : pen_bg));
781 redraw_mask |= REDRAW_FIELD;
783 if (mode == DL_LASER_ENABLED)
786 /* after the laser was deleted, the "damaged" graphics must be restored */
787 if (laser.num_damages)
789 int damage_start = 0;
792 /* determine the starting edge, from which graphics need to be restored */
795 for (i = 0; i < laser.num_damages; i++)
797 if (laser.damage[i].edge == start_edge + 1)
806 /* restore graphics from this starting edge to the end of damage list */
807 for (i = damage_start; i < laser.num_damages; i++)
809 int lx = laser.damage[i].x;
810 int ly = laser.damage[i].y;
811 int element = Feld[lx][ly];
813 if (Hit[lx][ly] == laser.damage[i].edge)
814 if (!((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
817 if (Box[lx][ly] == laser.damage[i].edge)
820 if (IS_DRAWABLE(element))
821 DrawField_MM(lx, ly);
824 elx = laser.damage[damage_start].x;
825 ely = laser.damage[damage_start].y;
826 element = Feld[elx][ely];
829 if (IS_BEAMER(element))
833 for (i = 0; i < laser.num_beamers; i++)
834 printf("-> %d\n", laser.beamer_edge[i]);
835 printf("DrawLaserExt: IS_BEAMER: [%d]: Hit[%d][%d] == %d [%d]\n",
836 mode, elx, ely, Hit[elx][ely], start_edge);
837 printf("DrawLaserExt: IS_BEAMER: %d / %d\n",
838 get_element_angle(element), laser.damage[damage_start].angle);
842 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
843 laser.num_beamers > 0 &&
844 start_edge == laser.beamer_edge[laser.num_beamers - 1])
846 /* element is outgoing beamer */
847 laser.num_damages = damage_start + 1;
849 if (IS_BEAMER(element))
850 laser.current_angle = get_element_angle(element);
854 /* element is incoming beamer or other element */
855 laser.num_damages = damage_start;
856 laser.current_angle = laser.damage[laser.num_damages].angle;
861 /* no damages but McDuffin himself (who needs to be redrawn anyway) */
863 elx = laser.start_edge.x;
864 ely = laser.start_edge.y;
865 element = Feld[elx][ely];
868 laser.num_edges = start_edge + 1;
870 laser.current_angle = laser.start_angle;
872 LX = laser.edge[start_edge].x - (SX + 2);
873 LY = laser.edge[start_edge].y - (SY + 2);
874 XS = 2 * Step[laser.current_angle].x;
875 YS = 2 * Step[laser.current_angle].y;
878 printf("DrawLaser: Set (LX, LY) to (%d, %d) [%d]\n",
884 if (IS_BEAMER(element) ||
885 IS_FIBRE_OPTIC(element) ||
886 IS_PACMAN(element) ||
888 IS_POLAR_CROSS(element) ||
889 element == EL_FUSE_ON)
894 printf("element == %d\n", element);
897 if (IS_22_5_ANGLE(laser.current_angle)) /* neither 90° nor 45° angle */
898 step_size = ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) ? 4 : 3);
902 if (IS_POLAR(element) || IS_POLAR_CROSS(element) ||
903 ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
904 (laser.num_beamers == 0 ||
905 start_edge != laser.beamer_edge[laser.num_beamers - 1])))
907 /* element is incoming beamer or other element */
908 step_size = -step_size;
913 if (IS_BEAMER(element))
915 printf("start_edge == %d, laser.beamer_edge == %d\n",
916 start_edge, laser.beamer_edge);
920 LX += step_size * XS;
921 LY += step_size * YS;
923 else if (element != EL_EMPTY)
932 printf("DrawLaser: Finally: (LX, LY) to (%d, %d) [%d]\n",
937 void DrawLaser(int start_edge, int mode)
939 if (laser.num_edges - start_edge < 0)
941 Error(ERR_WARN, "DrawLaser: laser.num_edges - start_edge < 0");
946 /* check if laser is interrupted by beamer element */
947 if (laser.num_beamers > 0 &&
948 start_edge < laser.beamer_edge[laser.num_beamers - 1])
950 if (mode == DL_LASER_ENABLED)
953 int tmp_start_edge = start_edge;
955 /* draw laser segments forward from the start to the last beamer */
956 for (i = 0; i < laser.num_beamers; i++)
958 int tmp_num_edges = laser.beamer_edge[i] - tmp_start_edge;
960 if (tmp_num_edges <= 0)
964 printf("DrawLaser: DL_LASER_ENABLED: i==%d: %d, %d\n",
965 i, laser.beamer_edge[i], tmp_start_edge);
968 DrawLaserExt(tmp_start_edge, tmp_num_edges, DL_LASER_ENABLED);
970 tmp_start_edge = laser.beamer_edge[i];
973 /* draw last segment from last beamer to the end */
974 DrawLaserExt(tmp_start_edge, laser.num_edges - tmp_start_edge,
980 int last_num_edges = laser.num_edges;
981 int num_beamers = laser.num_beamers;
983 /* delete laser segments backward from the end to the first beamer */
984 for (i = num_beamers-1; i >= 0; i--)
986 int tmp_num_edges = last_num_edges - laser.beamer_edge[i];
988 if (laser.beamer_edge[i] - start_edge <= 0)
991 DrawLaserExt(laser.beamer_edge[i], tmp_num_edges, DL_LASER_DISABLED);
993 last_num_edges = laser.beamer_edge[i];
998 if (last_num_edges - start_edge <= 0)
999 printf("DrawLaser: DL_LASER_DISABLED: %d, %d\n",
1000 last_num_edges, start_edge);
1003 /* delete first segment from start to the first beamer */
1004 DrawLaserExt(start_edge, last_num_edges - start_edge, DL_LASER_DISABLED);
1009 DrawLaserExt(start_edge, laser.num_edges - start_edge, mode);
1013 boolean HitElement(int element, int hit_mask)
1015 if (HitOnlyAnEdge(element, hit_mask))
1018 if (IS_MOVING(ELX, ELY) || IS_BLOCKED(ELX, ELY))
1019 element = MovingOrBlocked2Element_MM(ELX, ELY);
1022 printf("HitElement (1): element == %d\n", element);
1026 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1027 printf("HitElement (%d): EXACT MATCH @ (%d, %d)\n", element, ELX, ELY);
1029 printf("HitElement (%d): FUZZY MATCH @ (%d, %d)\n", element, ELX, ELY);
1032 AddDamagedField(ELX, ELY);
1034 /* this is more precise: check if laser would go through the center */
1035 if ((ELX * TILEX + 14 - LX) * YS != (ELY * TILEY + 14 - LY) * XS)
1037 /* skip the whole element before continuing the scan */
1043 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1045 if (LX/TILEX > ELX || LY/TILEY > ELY)
1047 /* skipping scan positions to the right and down skips one scan
1048 position too much, because this is only the top left scan position
1049 of totally four scan positions (plus one to the right, one to the
1050 bottom and one to the bottom right) */
1060 printf("HitElement (2): element == %d\n", element);
1063 if (LX + 5 * XS < 0 ||
1073 printf("HitElement (3): element == %d\n", element);
1076 if (IS_POLAR(element) &&
1077 ((element - EL_POLAR_START) % 2 ||
1078 (element - EL_POLAR_START) / 2 != laser.current_angle % 8))
1080 PlaySoundStereo(SND_KINK, ST(ELX));
1082 laser.num_damages--;
1087 if (IS_POLAR_CROSS(element) &&
1088 (element - EL_POLAR_CROSS_START) != laser.current_angle % 4)
1090 PlaySoundStereo(SND_KINK, ST(ELX));
1092 laser.num_damages--;
1097 if (!IS_BEAMER(element) &&
1098 !IS_FIBRE_OPTIC(element) &&
1099 !IS_GRID_WOOD(element) &&
1100 element != EL_FUEL_EMPTY)
1103 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1104 printf("EXACT MATCH @ (%d, %d)\n", ELX, ELY);
1106 printf("FUZZY MATCH @ (%d, %d)\n", ELX, ELY);
1109 LX = ELX * TILEX + 14;
1110 LY = ELY * TILEY + 14;
1112 AddLaserEdge(LX, LY);
1115 if (IS_MIRROR(element) ||
1116 IS_MIRROR_FIXED(element) ||
1117 IS_POLAR(element) ||
1118 IS_POLAR_CROSS(element) ||
1119 IS_DF_MIRROR(element) ||
1120 IS_DF_MIRROR_AUTO(element) ||
1121 element == EL_PRISM ||
1122 element == EL_REFRACTOR)
1124 int current_angle = laser.current_angle;
1127 laser.num_damages--;
1129 AddDamagedField(ELX, ELY);
1131 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1134 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1136 if (IS_MIRROR(element) ||
1137 IS_MIRROR_FIXED(element) ||
1138 IS_DF_MIRROR(element) ||
1139 IS_DF_MIRROR_AUTO(element))
1140 laser.current_angle = get_mirrored_angle(laser.current_angle,
1141 get_element_angle(element));
1143 if (element == EL_PRISM || element == EL_REFRACTOR)
1144 laser.current_angle = RND(16);
1146 XS = 2 * Step[laser.current_angle].x;
1147 YS = 2 * Step[laser.current_angle].y;
1149 if (!IS_22_5_ANGLE(laser.current_angle)) /* 90° or 45° angle */
1154 LX += step_size * XS;
1155 LY += step_size * YS;
1158 /* draw sparkles on mirror */
1159 if ((IS_MIRROR(element) || IS_MIRROR_FIXED(element)) &&
1160 current_angle != laser.current_angle)
1162 MoveSprite(vp, &Pfeil[2], 4 + 16 * ELX, 5 + 16 * ELY + 1);
1166 if ((!IS_POLAR(element) && !IS_POLAR_CROSS(element)) &&
1167 current_angle != laser.current_angle)
1168 PlaySoundStereo(SND_LASER, ST(ELX));
1171 (get_opposite_angle(laser.current_angle) ==
1172 laser.damage[laser.num_damages - 1].angle ? TRUE : FALSE);
1174 return (laser.overloaded ? TRUE : FALSE);
1177 if (element == EL_FUEL_FULL)
1179 laser.stops_inside_element = TRUE;
1184 if (element == EL_BOMB || element == EL_MINE)
1186 PlaySoundStereo(SND_KINK, ST(ELX));
1188 if (element == EL_MINE)
1189 laser.overloaded = TRUE;
1192 if (element == EL_KETTLE ||
1193 element == EL_CELL ||
1194 element == EL_KEY ||
1195 element == EL_LIGHTBALL ||
1196 element == EL_PACMAN ||
1199 if (!IS_PACMAN(element))
1202 if (element == EL_PACMAN)
1205 if (element == EL_KETTLE || element == EL_CELL)
1207 if (game_mm.kettles_still_needed > 0)
1208 game_mm.kettles_still_needed--;
1212 if (game_mm.kettles_still_needed == 0)
1215 static int xy[4][2] =
1223 PlaySoundStereo(SND_KLING, ST(ELX));
1225 for (y = 0; y < lev_fieldy; y++)
1227 for (x = 0; x < lev_fieldx; x++)
1229 /* initiate opening animation of exit door */
1230 if (Feld[x][y] == EL_EXIT_CLOSED)
1231 Feld[x][y] = EL_EXIT_OPENING;
1233 /* remove field that blocks receiver */
1234 if (IS_RECEIVER(Feld[x][y]))
1236 int phase = Feld[x][y] - EL_RECEIVER_START;
1237 int blocking_x, blocking_y;
1239 blocking_x = x + xy[phase][0];
1240 blocking_y = y + xy[phase][1];
1242 if (IN_LEV_FIELD(blocking_x, blocking_y))
1244 Feld[blocking_x][blocking_y] = EL_EMPTY;
1246 DrawField_MM(blocking_x, blocking_y);
1252 DrawLaser(0, DL_LASER_ENABLED);
1255 else if (element == EL_KEY)
1259 else if (element == EL_LIGHTBALL)
1263 else if (IS_PACMAN(element))
1265 DeletePacMan(ELX, ELY);
1272 if (element == EL_LIGHTBULB_OFF || element == EL_LIGHTBULB_ON)
1274 PlaySoundStereo(SND_KINK, ST(ELX));
1276 DrawLaser(0, DL_LASER_ENABLED);
1278 if (Feld[ELX][ELY] == EL_LIGHTBULB_OFF)
1280 Feld[ELX][ELY] = EL_LIGHTBULB_ON;
1281 game_mm.lights_still_needed--;
1285 Feld[ELX][ELY] = EL_LIGHTBULB_OFF;
1286 game_mm.lights_still_needed++;
1289 DrawField_MM(ELX, ELY);
1290 DrawLaser(0, DL_LASER_ENABLED);
1295 laser.stops_inside_element = TRUE;
1301 printf("HitElement (4): element == %d\n", element);
1304 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1305 laser.num_beamers < MAX_NUM_BEAMERS &&
1306 laser.beamer[BEAMER_NR(element)][1].num)
1308 int beamer_angle = get_element_angle(element);
1309 int beamer_nr = BEAMER_NR(element);
1313 printf("HitElement (BEAMER): element == %d\n", element);
1316 laser.num_damages--;
1318 if (IS_FIBRE_OPTIC(element) ||
1319 laser.current_angle == get_opposite_angle(beamer_angle))
1323 LX = ELX * TILEX + 14;
1324 LY = ELY * TILEY + 14;
1326 AddLaserEdge(LX, LY);
1327 AddDamagedField(ELX, ELY);
1329 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1332 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1334 pos = (ELX == laser.beamer[beamer_nr][0].x &&
1335 ELY == laser.beamer[beamer_nr][0].y ? 1 : 0);
1336 ELX = laser.beamer[beamer_nr][pos].x;
1337 ELY = laser.beamer[beamer_nr][pos].y;
1338 LX = ELX * TILEX + 14;
1339 LY = ELY * TILEY + 14;
1341 if (IS_BEAMER(element))
1343 laser.current_angle = get_element_angle(Feld[ELX][ELY]);
1344 XS = 2 * Step[laser.current_angle].x;
1345 YS = 2 * Step[laser.current_angle].y;
1348 laser.beamer_edge[laser.num_beamers] = laser.num_edges;
1350 AddLaserEdge(LX, LY);
1351 AddDamagedField(ELX, ELY);
1353 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1356 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1358 if (laser.current_angle == (laser.current_angle >> 1) << 1)
1363 LX += step_size * XS;
1364 LY += step_size * YS;
1366 laser.num_beamers++;
1375 boolean HitOnlyAnEdge(int element, int hit_mask)
1377 /* check if the laser hit only the edge of an element and, if so, go on */
1380 printf("LX, LY, hit_mask == %d, %d, %d\n", LX, LY, hit_mask);
1383 if ((hit_mask == HIT_MASK_TOPLEFT ||
1384 hit_mask == HIT_MASK_TOPRIGHT ||
1385 hit_mask == HIT_MASK_BOTTOMLEFT ||
1386 hit_mask == HIT_MASK_BOTTOMRIGHT) &&
1387 laser.current_angle % 4) /* angle is not 90° */
1391 if (hit_mask == HIT_MASK_TOPLEFT)
1396 else if (hit_mask == HIT_MASK_TOPRIGHT)
1401 else if (hit_mask == HIT_MASK_BOTTOMLEFT)
1406 else /* (hit_mask == HIT_MASK_BOTTOMRIGHT) */
1412 AddDamagedField((LX + 2 * dx) / TILEX, (LY + 2 * dy) / TILEY);
1418 printf("[HitOnlyAnEdge() == TRUE]\n");
1425 printf("[HitOnlyAnEdge() == FALSE]\n");
1431 boolean HitPolarizer(int element, int hit_mask)
1433 if (HitOnlyAnEdge(element, hit_mask))
1436 if (IS_DF_GRID(element))
1438 int grid_angle = get_element_angle(element);
1441 printf("HitPolarizer: angle: grid == %d, laser == %d\n",
1442 grid_angle, laser.current_angle);
1445 AddLaserEdge(LX, LY);
1446 AddDamagedField(ELX, ELY);
1449 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1451 if (laser.current_angle == grid_angle ||
1452 laser.current_angle == get_opposite_angle(grid_angle))
1454 /* skip the whole element before continuing the scan */
1460 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1462 if (LX/TILEX > ELX || LY/TILEY > ELY)
1464 /* skipping scan positions to the right and down skips one scan
1465 position too much, because this is only the top left scan position
1466 of totally four scan positions (plus one to the right, one to the
1467 bottom and one to the bottom right) */
1473 AddLaserEdge(LX, LY);
1479 printf("HitPolarizer: LX, LY == %d, %d [%d, %d] [%d, %d]\n",
1481 LX / TILEX, LY / TILEY,
1482 LX % TILEX, LY % TILEY);
1487 else if (IS_GRID_STEEL_FIXED(element) || IS_GRID_STEEL_AUTO(element))
1489 return HitReflectingWalls(element, hit_mask);
1493 return HitAbsorbingWalls(element, hit_mask);
1496 else if (IS_GRID_STEEL(element))
1498 return HitReflectingWalls(element, hit_mask);
1500 else /* IS_GRID_WOOD */
1502 return HitAbsorbingWalls(element, hit_mask);
1508 boolean HitBlock(int element, int hit_mask)
1510 boolean check = FALSE;
1512 if ((element == EL_GATE_STONE || element == EL_GATE_WOOD) &&
1513 game_mm.num_keys == 0)
1516 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1519 int ex = ELX * TILEX + 14;
1520 int ey = ELY * TILEY + 14;
1524 for (i = 1; i < 32; i++)
1529 if ((x == ex || x == ex + 1) && (y == ey || y == ey + 1))
1534 if (check && (element == EL_BLOCK_WOOD || element == EL_GATE_WOOD))
1535 return HitAbsorbingWalls(element, hit_mask);
1539 AddLaserEdge(LX - XS, LY - YS);
1540 AddDamagedField(ELX, ELY);
1543 Box[ELX][ELY] = laser.num_edges;
1545 return HitReflectingWalls(element, hit_mask);
1548 if (element == EL_GATE_STONE || element == EL_GATE_WOOD)
1550 int xs = XS / 2, ys = YS / 2;
1551 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1552 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1554 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1555 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1557 laser.overloaded = (element == EL_GATE_STONE);
1562 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1563 (hit_mask == HIT_MASK_TOP ||
1564 hit_mask == HIT_MASK_LEFT ||
1565 hit_mask == HIT_MASK_RIGHT ||
1566 hit_mask == HIT_MASK_BOTTOM))
1567 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1568 hit_mask == HIT_MASK_BOTTOM),
1569 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1570 hit_mask == HIT_MASK_RIGHT));
1571 AddLaserEdge(LX, LY);
1577 if (element == EL_GATE_STONE && Box[ELX][ELY])
1579 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
1591 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1593 int xs = XS / 2, ys = YS / 2;
1594 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1595 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1597 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1598 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1600 laser.overloaded = (element == EL_BLOCK_STONE);
1605 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1606 (hit_mask == HIT_MASK_TOP ||
1607 hit_mask == HIT_MASK_LEFT ||
1608 hit_mask == HIT_MASK_RIGHT ||
1609 hit_mask == HIT_MASK_BOTTOM))
1610 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1611 hit_mask == HIT_MASK_BOTTOM),
1612 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1613 hit_mask == HIT_MASK_RIGHT));
1614 AddDamagedField(ELX, ELY);
1616 LX = ELX * TILEX + 14;
1617 LY = ELY * TILEY + 14;
1619 AddLaserEdge(LX, LY);
1621 laser.stops_inside_element = TRUE;
1629 boolean HitLaserSource(int element, int hit_mask)
1631 if (HitOnlyAnEdge(element, hit_mask))
1634 PlaySoundStereo(SND_AUTSCH, ST(ELX));
1635 laser.overloaded = TRUE;
1640 boolean HitLaserDestination(int element, int hit_mask)
1642 if (HitOnlyAnEdge(element, hit_mask))
1645 if (element != EL_EXIT_OPEN &&
1646 !(IS_RECEIVER(element) &&
1647 game_mm.kettles_still_needed == 0 &&
1648 laser.current_angle == get_opposite_angle(get_element_angle(element))))
1650 PlaySoundStereo(SND_HOLZ, ST(ELX));
1654 if (IS_RECEIVER(element) ||
1655 (IS_22_5_ANGLE(laser.current_angle) &&
1656 (ELX != (LX + 6 * XS) / TILEX ||
1657 ELY != (LY + 6 * YS) / TILEY ||
1666 LX = ELX * TILEX + 14;
1667 LY = ELY * TILEY + 14;
1669 laser.stops_inside_element = TRUE;
1672 AddLaserEdge(LX, LY);
1673 AddDamagedField(ELX, ELY);
1675 if (game_mm.lights_still_needed == 0)
1676 game_mm.level_solved = TRUE;
1681 boolean HitReflectingWalls(int element, int hit_mask)
1683 /* check if laser hits side of a wall with an angle that is not 90° */
1684 if (!IS_90_ANGLE(laser.current_angle) && (hit_mask == HIT_MASK_TOP ||
1685 hit_mask == HIT_MASK_LEFT ||
1686 hit_mask == HIT_MASK_RIGHT ||
1687 hit_mask == HIT_MASK_BOTTOM))
1689 PlaySoundStereo(SND_HUI, ST(ELX));
1694 if (!IS_DF_GRID(element))
1695 AddLaserEdge(LX, LY);
1697 /* check if laser hits wall with an angle of 45° */
1698 if (!IS_22_5_ANGLE(laser.current_angle))
1700 if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1703 laser.current_angle = get_mirrored_angle(laser.current_angle,
1706 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1709 laser.current_angle = get_mirrored_angle(laser.current_angle,
1713 AddLaserEdge(LX, LY);
1715 XS = 2 * Step[laser.current_angle].x;
1716 YS = 2 * Step[laser.current_angle].y;
1720 else if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1722 laser.current_angle = get_mirrored_angle(laser.current_angle,
1727 if (!IS_DF_GRID(element))
1728 AddLaserEdge(LX, LY);
1733 if (!IS_DF_GRID(element))
1734 AddLaserEdge(LX, LY + YS / 2);
1737 if (!IS_DF_GRID(element))
1738 AddLaserEdge(LX, LY);
1741 YS = 2 * Step[laser.current_angle].y;
1745 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1747 laser.current_angle = get_mirrored_angle(laser.current_angle,
1752 if (!IS_DF_GRID(element))
1753 AddLaserEdge(LX, LY);
1758 if (!IS_DF_GRID(element))
1759 AddLaserEdge(LX + XS / 2, LY);
1762 if (!IS_DF_GRID(element))
1763 AddLaserEdge(LX, LY);
1766 XS = 2 * Step[laser.current_angle].x;
1772 /* reflection at the edge of reflecting DF style wall */
1773 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1775 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1776 hit_mask == HIT_MASK_TOPRIGHT) ||
1777 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1778 hit_mask == HIT_MASK_TOPLEFT) ||
1779 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1780 hit_mask == HIT_MASK_BOTTOMLEFT) ||
1781 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1782 hit_mask == HIT_MASK_BOTTOMRIGHT))
1785 (hit_mask == HIT_MASK_TOPRIGHT || hit_mask == HIT_MASK_BOTTOMLEFT ?
1786 ANG_MIRROR_135 : ANG_MIRROR_45);
1788 PlaySoundStereo(SND_HUI, ST(ELX));
1790 AddDamagedField(ELX, ELY);
1791 AddLaserEdge(LX, LY);
1793 laser.current_angle = get_mirrored_angle(laser.current_angle,
1801 AddLaserEdge(LX, LY);
1807 /* reflection inside an edge of reflecting DF style wall */
1808 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1810 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1811 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT)) ||
1812 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1813 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMRIGHT)) ||
1814 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1815 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT)) ||
1816 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1817 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPLEFT)))
1820 (hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT) ||
1821 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT) ?
1822 ANG_MIRROR_135 : ANG_MIRROR_45);
1824 PlaySoundStereo(SND_HUI, ST(ELX));
1827 AddDamagedField(ELX, ELY);
1830 AddLaserEdge(LX - XS, LY - YS);
1831 AddLaserEdge(LX - XS + (ABS(XS) == 4 ? XS/2 : 0),
1832 LY - YS + (ABS(YS) == 4 ? YS/2 : 0));
1834 laser.current_angle = get_mirrored_angle(laser.current_angle,
1842 AddLaserEdge(LX, LY);
1848 /* check if laser hits DF style wall with an angle of 90° */
1849 if (IS_DF_WALL(element) && IS_90_ANGLE(laser.current_angle))
1851 if ((IS_HORIZ_ANGLE(laser.current_angle) &&
1852 (!(hit_mask & HIT_MASK_TOP) || !(hit_mask & HIT_MASK_BOTTOM))) ||
1853 (IS_VERT_ANGLE(laser.current_angle) &&
1854 (!(hit_mask & HIT_MASK_LEFT) || !(hit_mask & HIT_MASK_RIGHT))))
1856 static int last_LX = 0, last_LY = 0, last_hit_mask = 0;
1858 /* laser at last step touched nothing or the same side of the wall */
1859 if (LX != last_LX || LY != last_LY || hit_mask == last_hit_mask)
1861 AddDamagedField(ELX, ELY);
1868 last_hit_mask = hit_mask;
1875 if (!HitOnlyAnEdge(element, hit_mask))
1877 laser.overloaded = TRUE;
1885 boolean HitAbsorbingWalls(int element, int hit_mask)
1887 if (HitOnlyAnEdge(element, hit_mask))
1891 (hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT))
1893 AddLaserEdge(LX - XS, LY - YS);
1900 (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM))
1902 AddLaserEdge(LX - XS, LY - YS);
1908 if (IS_WALL_WOOD(element) ||
1909 IS_DF_WALL_WOOD(element) ||
1910 IS_GRID_WOOD(element) ||
1911 IS_GRID_WOOD_FIXED(element) ||
1912 IS_GRID_WOOD_AUTO(element) ||
1913 element == EL_FUSE_ON ||
1914 element == EL_BLOCK_WOOD ||
1915 element == EL_GATE_WOOD)
1917 PlaySoundStereo(SND_HOLZ, ST(ELX));
1922 if (IS_WALL_ICE(element))
1926 mask = (LX + XS) / MINI_TILEX - ELX * 2 + 1; /* Quadrant (horizontal) */
1927 mask <<= (((LY + YS) / MINI_TILEY - ELY * 2) > 0) * 2; /* || (vertical) */
1929 /* check if laser hits wall with an angle of 90° */
1930 if (IS_90_ANGLE(laser.current_angle))
1931 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1933 if (mask == 1 || mask == 2 || mask == 4 || mask == 8)
1937 for (i = 0; i < 4; i++)
1939 if (mask == (1 << i) && (XS > 0) == (i % 2) && (YS > 0) == (i / 2))
1940 mask = 15 - (8 >> i);
1941 else if (ABS(XS) == 4 &&
1943 (XS > 0) == (i % 2) &&
1944 (YS < 0) == (i / 2))
1945 mask = 3 + (i / 2) * 9;
1946 else if (ABS(YS) == 4 &&
1948 (XS < 0) == (i % 2) &&
1949 (YS > 0) == (i / 2))
1950 mask = 5 + (i % 2) * 5;
1954 laser.wall_mask = mask;
1956 else if (IS_WALL_AMOEBA(element))
1958 int elx = (LX - 2 * XS) / TILEX;
1959 int ely = (LY - 2 * YS) / TILEY;
1960 int element2 = Feld[elx][ely];
1963 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element2))
1965 laser.dest_element = EL_EMPTY;
1973 mask = (LX - 2 * XS) / 16 - ELX * 2 + 1;
1974 mask <<= ((LY - 2 * YS) / 16 - ELY * 2) * 2;
1976 if (IS_90_ANGLE(laser.current_angle))
1977 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1979 laser.dest_element = element2 | EL_WALL_AMOEBA;
1981 laser.wall_mask = mask;
1987 void OpenExit(int x, int y)
1991 if (!MovDelay[x][y]) /* next animation frame */
1992 MovDelay[x][y] = 4 * delay;
1994 if (MovDelay[x][y]) /* wait some time before next frame */
1999 phase = MovDelay[x][y] / delay;
2001 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2002 DrawGraphicAnimation_MM(x, y, IMG_MM_EXIT_OPENING, 3 - phase);
2004 if (!MovDelay[x][y])
2006 Feld[x][y] = EL_EXIT_OPEN;
2012 void OpenSurpriseBall(int x, int y)
2016 if (!MovDelay[x][y]) /* next animation frame */
2017 MovDelay[x][y] = 50 * delay;
2019 if (MovDelay[x][y]) /* wait some time before next frame */
2023 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2026 int graphic = el2gfx(Store[x][y]);
2028 int dx = RND(26), dy = RND(26);
2030 getGraphicSource(graphic, 0, &bitmap, &gx, &gy);
2032 BlitBitmap(bitmap, drawto, gx + dx, gy + dy, 6, 6,
2033 SX + x * TILEX + dx, SY + y * TILEY + dy);
2035 MarkTileDirty(x, y);
2038 if (!MovDelay[x][y])
2040 Feld[x][y] = Store[x][y];
2049 void MeltIce(int x, int y)
2054 if (!MovDelay[x][y]) /* next animation frame */
2055 MovDelay[x][y] = frames * delay;
2057 if (MovDelay[x][y]) /* wait some time before next frame */
2060 int wall_mask = Store2[x][y];
2061 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_ICE;
2064 phase = frames - MovDelay[x][y] / delay - 1;
2066 if (!MovDelay[x][y])
2070 Feld[x][y] = real_element & (wall_mask ^ 0xFF);
2071 Store[x][y] = Store2[x][y] = 0;
2073 DrawWalls_MM(x, y, Feld[x][y]);
2075 if (Feld[x][y] == EL_WALL_ICE)
2076 Feld[x][y] = EL_EMPTY;
2078 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
2079 if (laser.damage[i].is_mirror)
2083 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
2085 DrawLaser(0, DL_LASER_DISABLED);
2089 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2091 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2093 laser.redraw = TRUE;
2098 void GrowAmoeba(int x, int y)
2103 if (!MovDelay[x][y]) /* next animation frame */
2104 MovDelay[x][y] = frames * delay;
2106 if (MovDelay[x][y]) /* wait some time before next frame */
2109 int wall_mask = Store2[x][y];
2110 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_AMOEBA;
2113 phase = MovDelay[x][y] / delay;
2115 if (!MovDelay[x][y])
2117 Feld[x][y] = real_element;
2118 Store[x][y] = Store2[x][y] = 0;
2120 DrawWalls_MM(x, y, Feld[x][y]);
2121 DrawLaser(0, DL_LASER_ENABLED);
2123 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2125 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2130 static void Explode_MM(int x, int y, int phase, int mode)
2132 int num_phase = 9, delay = 2;
2133 int last_phase = num_phase * delay;
2134 int half_phase = (num_phase / 2) * delay;
2136 laser.redraw = TRUE;
2138 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2140 int center_element = Feld[x][y];
2142 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2144 /* put moving element to center field (and let it explode there) */
2145 center_element = MovingOrBlocked2Element_MM(x, y);
2146 RemoveMovingField_MM(x, y);
2148 Feld[x][y] = center_element;
2151 if (center_element == EL_BOMB || IS_MCDUFFIN(center_element))
2152 Store[x][y] = center_element;
2154 Store[x][y] = EL_EMPTY;
2156 Store2[x][y] = mode;
2157 Feld[x][y] = EL_EXPLODING_OPAQUE;
2158 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2164 Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
2166 if (phase == half_phase)
2168 Feld[x][y] = EL_EXPLODING_TRANSP;
2170 if (x == ELX && y == ELY)
2174 if (phase == last_phase)
2176 if (Store[x][y] == EL_BOMB)
2178 laser.num_damages--;
2179 DrawLaser(0, DL_LASER_DISABLED);
2180 laser.num_edges = 0;
2182 Bang_MM(laser.start_edge.x, laser.start_edge.y);
2183 Store[x][y] = EL_EMPTY;
2185 else if (IS_MCDUFFIN(Store[x][y]))
2187 game_mm.game_over = TRUE;
2188 game_mm.game_over_cause = GAME_OVER_BOMB;
2189 Store[x][y] = EL_EMPTY;
2192 Feld[x][y] = Store[x][y];
2193 Store[x][y] = Store2[x][y] = 0;
2194 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2196 InitField(x, y, FALSE);
2199 else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2201 int graphic = IMG_MM_DEFAULT_EXPLODING;
2202 int graphic_phase = (phase / delay - 1);
2206 if (Store2[x][y] == EX_KETTLE)
2208 if (graphic_phase < 3)
2210 graphic = IMG_MM_KETTLE_EXPLODING;
2212 else if (graphic_phase < 5)
2218 graphic = IMG_EMPTY;
2222 else if (Store2[x][y] == EX_SHORT)
2224 if (graphic_phase < 4)
2230 graphic = IMG_EMPTY;
2235 getGraphicSource(graphic, graphic_phase, &bitmap, &src_x, &src_y);
2237 BlitBitmap(bitmap, drawto_field, src_x, src_y, TILEX, TILEY,
2238 FX + x * TILEX, FY + y * TILEY);
2240 MarkTileDirty(x, y);
2244 static void Bang_MM(int x, int y)
2246 int element = Feld[x][y];
2247 int mode = EX_NORMAL;
2250 DrawLaser(0, DL_LASER_ENABLED);
2269 if (IS_PACMAN(element))
2270 PlaySoundStereo(SND_QUIEK, ST(x));
2271 else if (element == EL_BOMB || IS_MCDUFFIN(element))
2272 PlaySoundStereo(SND_ROAAAR, ST(x));
2273 else if (element == EL_KEY)
2274 PlaySoundStereo(SND_KLING, ST(x));
2276 PlaySoundStereo((mode == EX_SHORT ? SND_WHOOSH : SND_KABUMM), ST(x));
2278 Explode_MM(x, y, EX_PHASE_START, mode);
2281 void TurnRound(int x, int y)
2293 { 0, 0 }, { 0, 0 }, { 0, 0 },
2298 int left, right, back;
2302 { MV_DOWN, MV_UP, MV_RIGHT },
2303 { MV_UP, MV_DOWN, MV_LEFT },
2305 { MV_LEFT, MV_RIGHT, MV_DOWN },
2306 { 0,0,0 }, { 0,0,0 }, { 0,0,0 },
2307 { MV_RIGHT, MV_LEFT, MV_UP }
2310 int element = Feld[x][y];
2311 int old_move_dir = MovDir[x][y];
2312 int right_dir = turn[old_move_dir].right;
2313 int back_dir = turn[old_move_dir].back;
2314 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
2315 int right_x = x + right_dx, right_y = y + right_dy;
2317 if (element == EL_PACMAN)
2319 boolean can_turn_right = FALSE;
2321 if (IN_LEV_FIELD(right_x, right_y) &&
2322 IS_EATABLE4PACMAN(Feld[right_x][right_y]))
2323 can_turn_right = TRUE;
2326 MovDir[x][y] = right_dir;
2328 MovDir[x][y] = back_dir;
2334 static void StartMoving_MM(int x, int y)
2336 int element = Feld[x][y];
2341 if (CAN_MOVE(element))
2345 if (MovDelay[x][y]) /* wait some time before next movement */
2353 /* now make next step */
2355 Moving2Blocked_MM(x, y, &newx, &newy); /* get next screen position */
2357 if (element == EL_PACMAN &&
2358 IN_LEV_FIELD(newx, newy) && IS_EATABLE4PACMAN(Feld[newx][newy]) &&
2359 !ObjHit(newx, newy, HIT_POS_CENTER))
2361 Store[newx][newy] = Feld[newx][newy];
2362 Feld[newx][newy] = EL_EMPTY;
2364 DrawField_MM(newx, newy);
2366 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy) ||
2367 ObjHit(newx, newy, HIT_POS_CENTER))
2369 /* object was running against a wall */
2376 InitMovingField_MM(x, y, MovDir[x][y]);
2380 ContinueMoving_MM(x, y);
2383 static void ContinueMoving_MM(int x, int y)
2385 int element = Feld[x][y];
2386 int direction = MovDir[x][y];
2387 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2388 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2389 int horiz_move = (dx!=0);
2390 int newx = x + dx, newy = y + dy;
2391 int step = (horiz_move ? dx : dy) * TILEX / 8;
2393 MovPos[x][y] += step;
2395 if (ABS(MovPos[x][y]) >= TILEX) /* object reached its destination */
2397 Feld[x][y] = EL_EMPTY;
2398 Feld[newx][newy] = element;
2400 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2401 MovDelay[newx][newy] = 0;
2403 if (!CAN_MOVE(element))
2404 MovDir[newx][newy] = 0;
2407 DrawField_MM(newx, newy);
2409 Stop[newx][newy] = TRUE;
2411 if (element == EL_PACMAN)
2413 if (Store[newx][newy] == EL_BOMB)
2414 Bang_MM(newx, newy);
2416 if (IS_WALL_AMOEBA(Store[newx][newy]) &&
2417 (LX + 2 * XS) / TILEX == newx &&
2418 (LY + 2 * YS) / TILEY == newy)
2425 else /* still moving on */
2430 laser.redraw = TRUE;
2433 void ClickElement(int mx, int my, int button)
2435 static unsigned int click_delay = 0;
2436 static int click_delay_value = CLICK_DELAY_SHORT;
2437 static boolean new_button = TRUE;
2439 int x = (mx - SX) / TILEX, y = (my - SY) / TILEY;
2441 /* do not rotate objects hit by the laser after the game was solved */
2442 if (game_mm.level_solved && Hit[x][y])
2445 if (button == MB_RELEASED)
2448 click_delay_value = CLICK_DELAY_SHORT;
2450 /* release eventually hold auto-rotating mirror */
2451 RotateMirror(x, y, MB_RELEASED);
2456 if (!DelayReached(&click_delay, click_delay_value) && !new_button)
2459 if (button == MB_MIDDLEBUTTON) /* middle button has no function */
2462 if (!IN_PIX_FIELD(mx - SX, my - SY))
2465 if (Feld[x][y] == EL_EMPTY)
2468 element = Feld[x][y];
2470 if (IS_MIRROR(element) ||
2471 IS_BEAMER(element) ||
2472 IS_POLAR(element) ||
2473 IS_POLAR_CROSS(element) ||
2474 IS_DF_MIRROR(element) ||
2475 IS_DF_MIRROR_AUTO(element))
2477 RotateMirror(x, y, button);
2479 else if (IS_MCDUFFIN(element))
2481 if (!laser.fuse_off)
2483 DrawLaser(0, DL_LASER_DISABLED);
2490 element = get_rotated_element(element, BUTTON_ROTATION(button));
2491 laser.start_angle = get_element_angle(element);
2495 Feld[x][y] = element;
2502 if (!laser.fuse_off)
2505 else if (element == EL_FUSE_ON && laser.fuse_off)
2507 if (x != laser.fuse_x || y != laser.fuse_y)
2510 laser.fuse_off = FALSE;
2511 laser.fuse_x = laser.fuse_y = -1;
2513 DrawGraphic_MM(x, y, IMG_MM_FUSE_ACTIVE);
2516 else if (element == EL_FUSE_ON && !laser.fuse_off && new_button)
2518 laser.fuse_off = TRUE;
2521 laser.overloaded = FALSE;
2523 DrawLaser(0, DL_LASER_DISABLED);
2524 DrawGraphic_MM(x, y, IMG_MM_FUSE);
2526 else if (element == EL_LIGHTBALL)
2530 DrawLaser(0, DL_LASER_ENABLED);
2533 click_delay_value = (new_button ? CLICK_DELAY_LONG : CLICK_DELAY_SHORT);
2537 void RotateMirror(int x, int y, int button)
2539 static int hold_x = -1, hold_y = -1;
2541 if (button == MB_RELEASED)
2543 /* release eventually hold auto-rotating mirror */
2550 if (IS_MIRROR(Feld[x][y]) ||
2551 IS_POLAR_CROSS(Feld[x][y]) ||
2552 IS_POLAR(Feld[x][y]) ||
2553 IS_BEAMER(Feld[x][y]) ||
2554 IS_DF_MIRROR(Feld[x][y]) ||
2555 IS_GRID_STEEL_AUTO(Feld[x][y]) ||
2556 IS_GRID_WOOD_AUTO(Feld[x][y]))
2558 Feld[x][y] = get_rotated_element(Feld[x][y], BUTTON_ROTATION(button));
2560 else if (IS_DF_MIRROR_AUTO(Feld[x][y]))
2562 if (button == MB_LEFTBUTTON)
2564 /* left mouse button only for manual adjustment, no auto-rotating;
2565 freeze mirror for until mouse button released */
2569 else if (button == MB_RIGHTBUTTON && (hold_x != x || hold_y != y))
2571 Feld[x][y] = get_rotated_element(Feld[x][y], ROTATE_RIGHT);
2575 if (IS_GRID_STEEL_AUTO(Feld[x][y]) || IS_GRID_WOOD_AUTO(Feld[x][y]))
2577 int edge = Hit[x][y];
2583 DrawLaser(edge - 1, DL_LASER_DISABLED);
2587 else if (ObjHit(x, y, HIT_POS_CENTER))
2589 int edge = Hit[x][y];
2593 Error(ERR_WARN, "RotateMirror: inconsistent field Hit[][]!\n");
2597 DrawLaser(edge - 1, DL_LASER_DISABLED);
2604 if (ObjHit(x, y, HIT_POS_EDGE | HIT_POS_BETWEEN))
2609 if ((IS_BEAMER(Feld[x][y]) ||
2610 IS_POLAR(Feld[x][y]) ||
2611 IS_POLAR_CROSS(Feld[x][y])) && x == ELX && y == ELY)
2615 if (IS_BEAMER(Feld[x][y]))
2618 printf("TEST (%d, %d) [%d] [%d]\n",
2620 laser.beamer_edge, laser.beamer[1].num);
2630 DrawLaser(0, DL_LASER_ENABLED);
2634 void AutoRotateMirrors()
2636 static unsigned int rotate_delay = 0;
2639 if (!DelayReached(&rotate_delay, AUTO_ROTATE_DELAY))
2642 for (x = 0; x < lev_fieldx; x++)
2644 for (y = 0; y < lev_fieldy; y++)
2646 int element = Feld[x][y];
2648 /* do not rotate objects hit by the laser after the game was solved */
2649 if (game_mm.level_solved && Hit[x][y])
2652 if (IS_DF_MIRROR_AUTO(element) ||
2653 IS_GRID_WOOD_AUTO(element) ||
2654 IS_GRID_STEEL_AUTO(element) ||
2655 element == EL_REFRACTOR)
2656 RotateMirror(x, y, MB_RIGHTBUTTON);
2661 boolean ObjHit(int obx, int oby, int bits)
2668 if (bits & HIT_POS_CENTER)
2670 if (ReadPixel(drawto, SX + obx + 15, SY + oby + 15) == pen_ray)
2674 if (bits & HIT_POS_EDGE)
2676 for (i = 0; i < 4; i++)
2677 if (ReadPixel(drawto,
2678 SX + obx + 31 * (i % 2),
2679 SY + oby + 31 * (i / 2)) == pen_ray)
2683 if (bits & HIT_POS_BETWEEN)
2685 for (i = 0; i < 4; i++)
2686 if (ReadPixel(drawto,
2687 SX + 4 + obx + 22 * (i % 2),
2688 SY + 4 + oby + 22 * (i / 2)) == pen_ray)
2695 void DeletePacMan(int px, int py)
2701 if (game_mm.num_pacman <= 1)
2703 game_mm.num_pacman = 0;
2707 for (i = 0; i < game_mm.num_pacman; i++)
2708 if (game_mm.pacman[i].x == px && game_mm.pacman[i].y == py)
2711 game_mm.num_pacman--;
2713 for (j = i; j < game_mm.num_pacman; j++)
2715 game_mm.pacman[j].x = game_mm.pacman[j + 1].x;
2716 game_mm.pacman[j].y = game_mm.pacman[j + 1].y;
2717 game_mm.pacman[j].dir = game_mm.pacman[j + 1].dir;
2721 void ColorCycling(void)
2723 static int CC, Cc = 0;
2725 static int color, old = 0xF00, new = 0x010, mult = 1;
2726 static unsigned short red, green, blue;
2728 if (color_status == STATIC_COLORS)
2733 if (CC < Cc || CC > Cc + 50)
2737 color = old + new * mult;
2743 if (ABS(mult) == 16)
2753 red = 0x0e00 * ((color & 0xF00) >> 8);
2754 green = 0x0e00 * ((color & 0x0F0) >> 4);
2755 blue = 0x0e00 * ((color & 0x00F));
2756 SetRGB(pen_magicolor[0], red, green, blue);
2758 red = 0x1111 * ((color & 0xF00) >> 8);
2759 green = 0x1111 * ((color & 0x0F0) >> 4);
2760 blue = 0x1111 * ((color & 0x00F));
2761 SetRGB(pen_magicolor[1], red, green, blue);
2765 static void GameActions_MM_Ext(byte action[MAX_PLAYERS], boolean warp_mode)
2767 static unsigned int action_delay = 0;
2768 static unsigned int pacman_delay = 0;
2769 static unsigned int energy_delay = 0;
2770 static unsigned int overload_delay = 0;
2776 WaitUntilDelayReached(&action_delay, GameFrameDelay);
2778 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2781 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2783 element = Feld[x][y];
2785 if (!IS_MOVING(x, y) && CAN_MOVE(element))
2786 StartMoving_MM(x, y);
2787 else if (IS_MOVING(x, y))
2788 ContinueMoving_MM(x, y);
2789 else if (IS_EXPLODING(element))
2790 Explode_MM(x, y, Frame[x][y], EX_NORMAL);
2791 else if (element == EL_EXIT_OPENING)
2793 else if (element == EL_GRAY_BALL_OPENING)
2794 OpenSurpriseBall(x, y);
2795 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_ICE)
2797 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_AMOEBA)
2801 AutoRotateMirrors();
2804 /* !!! CHANGE THIS: REDRAW ONLY WHEN NEEDED !!! */
2806 /* redraw after Explode_MM() ... */
2808 DrawLaser(0, DL_LASER_ENABLED);
2809 laser.redraw = FALSE;
2814 if (game_mm.num_pacman && DelayReached(&pacman_delay, 250))
2818 if (laser.num_damages > MAX_LASER_LEN && !laser.fuse_off)
2820 DrawLaser(0, DL_LASER_DISABLED);
2825 if (DelayReached(&energy_delay, 4000))
2827 game_mm.energy_left--;
2828 if (game_mm.energy_left >= 0)
2831 BlitBitmap(pix[PIX_DOOR], drawto,
2832 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
2833 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
2834 DX_ENERGY, DY_ENERGY);
2836 redraw_mask |= REDRAW_DOOR_1;
2838 else if (setup.time_limit)
2842 for (i = 15; i >= 0; i--)
2845 SetRGB(pen_ray, 0x0000, 0x0000, i * color_scale);
2847 pen_ray = GetPixelFromRGB(window,
2848 native_mm_level.laser_red * 0x11 * i,
2849 native_mm_level.laser_green * 0x11 * i,
2850 native_mm_level.laser_blue * 0x11 * i);
2852 DrawLaser(0, DL_LASER_ENABLED);
2857 StopSound(SND_WARNTON);
2860 DrawLaser(0, DL_LASER_DISABLED);
2861 game_mm.game_over = TRUE;
2862 game_mm.game_over_cause = GAME_OVER_NO_ENERGY;
2865 if (Request("Out of magic energy ! Play it again ?",
2866 REQ_ASK | REQ_STAY_CLOSED))
2872 game_status = MAINMENU;
2881 element = laser.dest_element;
2884 if (element != Feld[ELX][ELY])
2886 printf("element == %d, Feld[ELX][ELY] == %d\n",
2887 element, Feld[ELX][ELY]);
2891 if (!laser.overloaded && laser.overload_value == 0 &&
2892 element != EL_BOMB &&
2893 element != EL_MINE &&
2894 element != EL_BALL_GRAY &&
2895 element != EL_BLOCK_STONE &&
2896 element != EL_BLOCK_WOOD &&
2897 element != EL_FUSE_ON &&
2898 element != EL_FUEL_FULL &&
2899 !IS_WALL_ICE(element) &&
2900 !IS_WALL_AMOEBA(element))
2903 if (((laser.overloaded && laser.overload_value < MAX_LASER_OVERLOAD) ||
2904 (!laser.overloaded && laser.overload_value > 0)) &&
2905 DelayReached(&overload_delay, 60 + !laser.overloaded * 120))
2907 if (laser.overloaded)
2908 laser.overload_value++;
2910 laser.overload_value--;
2912 if (game_mm.cheat_no_overload)
2914 laser.overloaded = FALSE;
2915 laser.overload_value = 0;
2918 game_mm.laser_overload_value = laser.overload_value;
2920 if (laser.overload_value < MAX_LASER_OVERLOAD - 8)
2922 int color_up = 0xFF * laser.overload_value / MAX_LASER_OVERLOAD;
2923 int color_down = 0xFF - color_up;
2926 SetRGB(pen_ray, (laser.overload_value / 6) * color_scale, 0x0000,
2927 (15 - (laser.overload_value / 6)) * color_scale);
2930 GetPixelFromRGB(window,
2931 (native_mm_level.laser_red ? 0xFF : color_up),
2932 (native_mm_level.laser_green ? color_down : 0x00),
2933 (native_mm_level.laser_blue ? color_down : 0x00));
2935 DrawLaser(0, DL_LASER_ENABLED);
2939 if (laser.overloaded)
2941 if (setup.sound_loops)
2942 PlaySoundExt(SND_WARNTON, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
2943 SND_CTRL_PLAY_LOOP);
2945 PlaySoundStereo(SND_WARNTON, SOUND_MAX_RIGHT);
2948 if (!laser.overloaded)
2949 StopSound(SND_WARNTON);
2951 if (laser.overloaded)
2954 BlitBitmap(pix[PIX_DOOR], drawto,
2955 DOOR_GFX_PAGEX4 + XX_OVERLOAD,
2956 DOOR_GFX_PAGEY1 + YY_OVERLOAD + OVERLOAD_YSIZE
2957 - laser.overload_value,
2958 OVERLOAD_XSIZE, laser.overload_value,
2959 DX_OVERLOAD, DY_OVERLOAD + OVERLOAD_YSIZE
2960 - laser.overload_value);
2962 redraw_mask |= REDRAW_DOOR_1;
2967 BlitBitmap(pix[PIX_DOOR], drawto,
2968 DOOR_GFX_PAGEX5 + XX_OVERLOAD, DOOR_GFX_PAGEY1 + YY_OVERLOAD,
2969 OVERLOAD_XSIZE, OVERLOAD_YSIZE - laser.overload_value,
2970 DX_OVERLOAD, DY_OVERLOAD);
2972 redraw_mask |= REDRAW_DOOR_1;
2975 if (laser.overload_value == MAX_LASER_OVERLOAD)
2979 for (i = 15; i >= 0; i--)
2982 SetRGB(pen_ray, i * color_scale, 0x0000, 0x0000);
2985 pen_ray = GetPixelFromRGB(window, 0x11 * i, 0x00, 0x00);
2987 DrawLaser(0, DL_LASER_ENABLED);
2992 DrawLaser(0, DL_LASER_DISABLED);
2994 game_mm.game_over = TRUE;
2995 game_mm.game_over_cause = GAME_OVER_OVERLOADED;
2998 if (Request("Magic spell hit Mc Duffin ! Play it again ?",
2999 REQ_ASK | REQ_STAY_CLOSED))
3005 game_status = MAINMENU;
3019 if (element == EL_BOMB && CT > 1500)
3021 if (game_mm.cheat_no_explosion)
3025 laser.num_damages--;
3026 DrawLaser(0, DL_LASER_DISABLED);
3027 laser.num_edges = 0;
3032 laser.dest_element = EL_EXPLODING_OPAQUE;
3036 laser.num_damages--;
3037 DrawLaser(0, DL_LASER_DISABLED);
3039 laser.num_edges = 0;
3040 Bang_MM(laser.start_edge.x, laser.start_edge.y);
3042 if (Request("Bomb killed Mc Duffin ! Play it again ?",
3043 REQ_ASK | REQ_STAY_CLOSED))
3049 game_status = MAINMENU;
3057 if (element == EL_FUSE_ON && CT > 500)
3059 laser.fuse_off = TRUE;
3063 DrawLaser(0, DL_LASER_DISABLED);
3064 DrawGraphic_MM(ELX, ELY, IMG_MM_FUSE);
3067 if (element == EL_BALL_GRAY && CT > 1500)
3069 static int new_elements[] =
3072 EL_MIRROR_FIXED_START,
3074 EL_POLAR_CROSS_START,
3080 int num_new_elements = sizeof(new_elements) / sizeof(int);
3081 int new_element = new_elements[RND(num_new_elements)];
3083 Store[ELX][ELY] = new_element + RND(get_num_elements(new_element));
3084 Feld[ELX][ELY] = EL_GRAY_BALL_OPENING;
3086 /* !!! CHECK AGAIN: Laser on Polarizer !!! */
3097 element = EL_MIRROR_START + RND(16);
3103 element = (rnd == 0 ? EL_KETTLE : rnd == 1 ? EL_BOMB : EL_PRISM);
3110 element = (rnd == 0 ? EL_FUSE_ON :
3111 rnd >= 1 && rnd <= 4 ? EL_PACMAN_RIGHT + rnd - 1 :
3112 rnd >= 5 && rnd <= 20 ? EL_POLAR_START + rnd - 5 :
3113 rnd >= 21 && rnd <= 24 ? EL_POLAR_CROSS_START + rnd - 21 :
3114 EL_MIRROR_FIXED_START + rnd - 25);
3119 graphic = el2gfx(element);
3121 for (i = 0; i < 50; i++)
3127 BlitBitmap(pix[PIX_BACK], drawto,
3128 SX + (graphic % GFX_PER_LINE) * TILEX + x,
3129 SY + (graphic / GFX_PER_LINE) * TILEY + y, 6, 6,
3130 SX + ELX * TILEX + x,
3131 SY + ELY * TILEY + y);
3133 MarkTileDirty(ELX, ELY);
3136 DrawLaser(0, DL_LASER_ENABLED);
3141 Feld[ELX][ELY] = element;
3142 DrawField_MM(ELX, ELY);
3145 printf("NEW ELEMENT: (%d, %d)\n", ELX, ELY);
3148 /* above stuff: GRAY BALL -> PRISM !!! */
3150 LX = ELX * TILEX + 14;
3151 LY = ELY * TILEY + 14;
3152 if (laser.current_angle == (laser.current_angle >> 1) << 1)
3159 laser.num_edges -= 2;
3160 laser.num_damages--;
3164 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
3165 if (laser.damage[i].is_mirror)
3169 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3171 DrawLaser(0, DL_LASER_DISABLED);
3173 DrawLaser(0, DL_LASER_DISABLED);
3179 printf("TEST ELEMENT: %d\n", Feld[0][0]);
3186 if (IS_WALL_ICE(element) && CT > 1000)
3188 PlaySoundStereo(SND_SLURP, ST(ELX));
3191 Feld[ELX][ELY] = Feld[ELX][ELY] - EL_WALL_ICE + EL_WALL_CHANGING;
3192 Store[ELX][ELY] = EL_WALL_ICE;
3193 Store2[ELX][ELY] = laser.wall_mask;
3195 laser.dest_element = Feld[ELX][ELY];
3200 for (i = 0; i < 5; i++)
3206 Feld[ELX][ELY] &= (laser.wall_mask ^ 0xFF);
3210 DrawWallsAnimation_MM(ELX, ELY, Feld[ELX][ELY], phase, laser.wall_mask);
3215 if (Feld[ELX][ELY] == EL_WALL_ICE)
3216 Feld[ELX][ELY] = EL_EMPTY;
3220 LX = laser.edge[laser.num_edges].x - (SX + 2);
3221 LY = laser.edge[laser.num_edges].y - (SY + 2);
3224 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
3225 if (laser.damage[i].is_mirror)
3229 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3231 DrawLaser(0, DL_LASER_DISABLED);
3238 if (IS_WALL_AMOEBA(element) && CT > 1200)
3240 int k1, k2, k3, dx, dy, de, dm;
3241 int element2 = Feld[ELX][ELY];
3243 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element))
3246 for (i = laser.num_damages - 1; i >= 0; i--)
3247 if (laser.damage[i].is_mirror)
3250 r = laser.num_edges;
3251 d = laser.num_damages;
3258 DrawLaser(laser.damage[k1].edge - 1, DL_LASER_DISABLED);
3261 DrawLaser(0, DL_LASER_ENABLED);
3264 x = laser.damage[k1].x;
3265 y = laser.damage[k1].y;
3270 for (i = 0; i < 4; i++)
3272 if (laser.wall_mask & (1 << i))
3274 if (ReadPixel(drawto,
3275 SX + ELX * TILEX + 14 + (i % 2) * 2,
3276 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3278 if (ReadPixel(drawto,
3279 SX + ELX * TILEX + 31 * (i % 2),
3280 SY + ELY * TILEY + 14 + (i / 2) * 2) == pen_ray)
3287 for (i = 0; i < 4; i++)
3289 if (laser.wall_mask & (1 << i))
3291 if (ReadPixel(drawto,
3292 SX + ELX * TILEX + 31 * (i % 2),
3293 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3300 if (laser.num_beamers > 0 ||
3301 k1 < 1 || k2 < 4 || k3 < 4 ||
3302 ReadPixel(drawto, SX + ELX * TILEX + 14, SY + ELY * TILEY + 14)
3305 laser.num_edges = r;
3306 laser.num_damages = d;
3308 DrawLaser(0, DL_LASER_DISABLED);
3311 Feld[ELX][ELY] = element | laser.wall_mask;
3315 de = Feld[ELX][ELY];
3316 dm = laser.wall_mask;
3320 int x = ELX, y = ELY;
3321 int wall_mask = laser.wall_mask;
3324 DrawLaser(0, DL_LASER_ENABLED);
3326 PlaySoundStereo(SND_AMOEBE, ST(dx));
3328 Feld[x][y] = Feld[x][y] - EL_WALL_AMOEBA + EL_WALL_CHANGING;
3329 Store[x][y] = EL_WALL_AMOEBA;
3330 Store2[x][y] = wall_mask;
3336 DrawWallsAnimation_MM(dx, dy, de, 4, dm);
3338 DrawLaser(0, DL_LASER_ENABLED);
3340 PlaySoundStereo(SND_AMOEBE, ST(dx));
3342 for (i = 4; i >= 0; i--)
3344 DrawWallsAnimation_MM(dx, dy, de, i, dm);
3350 DrawLaser(0, DL_LASER_ENABLED);
3355 if ((element == EL_BLOCK_WOOD || element == EL_BLOCK_STONE) &&
3356 laser.stops_inside_element && CT > 1500)
3361 if (ABS(XS) > ABS(YS))
3368 for (i = 0; i < 4; i++)
3375 x = ELX + Step[k * 4].x;
3376 y = ELY + Step[k * 4].y;
3378 if (!IN_LEV_FIELD(x, y) || Feld[x][y] != EL_EMPTY)
3381 if (ObjHit(x, y, HIT_POS_CENTER | HIT_POS_EDGE | HIT_POS_BETWEEN))
3389 laser.overloaded = (element == EL_BLOCK_STONE);
3394 PlaySoundStereo(SND_BONG, ST(ELX));
3397 Feld[x][y] = element;
3399 DrawGraphic_MM(ELX, ELY, IMG_EMPTY);
3402 if (element == EL_BLOCK_STONE && Box[ELX][ELY])
3404 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
3405 DrawLaser(laser.num_edges - 1, DL_LASER_ENABLED);
3413 if (element == EL_FUEL_FULL && CT > 200)
3415 for (i = game_mm.energy_left; i <= MAX_LASER_ENERGY; i+=2)
3418 BlitBitmap(pix[PIX_DOOR], drawto,
3419 DOOR_GFX_PAGEX4 + XX_ENERGY,
3420 DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
3421 ENERGY_XSIZE, i, DX_ENERGY,
3422 DY_ENERGY + ENERGY_YSIZE - i);
3425 redraw_mask |= REDRAW_DOOR_1;
3431 game_mm.energy_left = MAX_LASER_ENERGY;
3432 Feld[ELX][ELY] = EL_FUEL_EMPTY;
3433 DrawField_MM(ELX, ELY);
3435 DrawLaser(0, DL_LASER_ENABLED);
3443 void GameActions_MM(byte action[MAX_PLAYERS], boolean warp_mode)
3446 ClickElement(0, 0, MB_NOT_PRESSED);
3448 GameActions_MM_Ext(action, warp_mode);
3454 int mx, my, ox, oy, nx, ny;
3458 if (++p >= game_mm.num_pacman)
3461 game_mm.pacman[p].dir--;
3463 for (l = 1; l < 5; l++)
3465 game_mm.pacman[p].dir++;
3467 if (game_mm.pacman[p].dir > 4)
3468 game_mm.pacman[p].dir = 1;
3470 if (game_mm.pacman[p].dir % 2)
3473 my = game_mm.pacman[p].dir - 2;
3478 mx = 3 - game_mm.pacman[p].dir;
3481 ox = game_mm.pacman[p].x;
3482 oy = game_mm.pacman[p].y;
3485 element = Feld[nx][ny];
3487 if (nx < 0 || nx > 15 || ny < 0 || ny > 11)
3490 if (!IS_EATABLE4PACMAN(element))
3493 if (ObjHit(nx, ny, HIT_POS_CENTER))
3496 Feld[ox][oy] = EL_EMPTY;
3498 EL_PACMAN_RIGHT - 1 +
3499 (game_mm.pacman[p].dir - 1 +
3500 (game_mm.pacman[p].dir % 2) * 2);
3502 game_mm.pacman[p].x = nx;
3503 game_mm.pacman[p].y = ny;
3505 DrawGraphic_MM(ox, oy, IMG_EMPTY);
3507 if (element != EL_EMPTY)
3509 int graphic = el2gfx(Feld[nx][ny]);
3514 getGraphicSource(graphic, 0, &bitmap, &src_x, &src_y);
3517 ox = SX + ox * TILEX;
3518 oy = SY + oy * TILEY;
3520 for (i = 1; i < 33; i += 2)
3521 BlitBitmap(bitmap, window,
3522 src_x, src_y, TILEX, TILEY,
3523 ox + i * mx, oy + i * my);
3524 Ct = Ct + Counter() - CT;
3527 DrawField_MM(nx, ny);
3530 if (!laser.fuse_off)
3532 DrawLaser(0, DL_LASER_ENABLED);
3534 if (ObjHit(nx, ny, HIT_POS_BETWEEN))
3536 AddDamagedField(nx, ny);
3538 laser.damage[laser.num_damages - 1].edge = 0;
3542 if (element == EL_BOMB)
3543 DeletePacMan(nx, ny);
3545 if (IS_WALL_AMOEBA(element) &&
3546 (LX + 2 * XS) / TILEX == nx &&
3547 (LY + 2 * YS) / TILEY == ny)
3560 boolean raise_level = FALSE;
3563 if (local_player->MovPos)
3566 local_player->LevelSolved = FALSE;
3569 if (game_mm.energy_left)
3571 if (setup.sound_loops)
3572 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3573 SND_CTRL_PLAY_LOOP);
3575 while (game_mm.energy_left > 0)
3577 if (!setup.sound_loops)
3578 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3581 if (game_mm.energy_left > 0 && !(game_mm.energy_left % 10))
3582 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3587 game_mm.energy_left--;
3588 if (game_mm.energy_left >= 0)
3591 BlitBitmap(pix[PIX_DOOR], drawto,
3592 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
3593 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
3594 DX_ENERGY, DY_ENERGY);
3596 redraw_mask |= REDRAW_DOOR_1;
3603 if (setup.sound_loops)
3604 StopSound(SND_SIRR);
3606 else if (native_mm_level.time == 0) /* level without time limit */
3608 if (setup.sound_loops)
3609 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3610 SND_CTRL_PLAY_LOOP);
3612 while (TimePlayed < 999)
3614 if (!setup.sound_loops)
3615 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3616 if (TimePlayed < 999 && !(TimePlayed % 10))
3617 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3618 if (TimePlayed < 900 && !(TimePlayed % 10))
3624 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
3631 if (setup.sound_loops)
3632 StopSound(SND_SIRR);
3639 CloseDoor(DOOR_CLOSE_1);
3641 Request("Level solved !", REQ_CONFIRM);
3643 if (level_nr == leveldir_current->handicap_level)
3645 leveldir_current->handicap_level++;
3646 SaveLevelSetup_SeriesInfo();
3649 if (level_editor_test_game)
3650 game_mm.score = -1; /* no highscore when playing from editor */
3651 else if (level_nr < leveldir_current->last_level)
3652 raise_level = TRUE; /* advance to next level */
3654 if ((hi_pos = NewHiScore_MM()) >= 0)
3656 game_status = HALLOFFAME;
3658 // DrawHallOfFame(hi_pos);
3665 game_status = MAINMENU;
3681 // LoadScore(level_nr);
3683 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
3684 game_mm.score < highscore[MAX_SCORE_ENTRIES - 1].Score)
3687 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3689 if (game_mm.score > highscore[k].Score)
3691 /* player has made it to the hall of fame */
3693 if (k < MAX_SCORE_ENTRIES - 1)
3695 int m = MAX_SCORE_ENTRIES - 1;
3698 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3699 if (!strcmp(setup.player_name, highscore[l].Name))
3701 if (m == k) /* player's new highscore overwrites his old one */
3705 for (l = m; l>k; l--)
3707 strcpy(highscore[l].Name, highscore[l - 1].Name);
3708 highscore[l].Score = highscore[l - 1].Score;
3715 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3716 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3717 highscore[k].Score = game_mm.score;
3724 else if (!strncmp(setup.player_name, highscore[k].Name,
3725 MAX_PLAYER_NAME_LEN))
3726 break; /* player already there with a higher score */
3731 // if (position >= 0)
3732 // SaveScore(level_nr);
3737 static void InitMovingField_MM(int x, int y, int direction)
3739 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3740 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3742 MovDir[x][y] = direction;
3743 MovDir[newx][newy] = direction;
3745 if (Feld[newx][newy] == EL_EMPTY)
3746 Feld[newx][newy] = EL_BLOCKED;
3749 static void Moving2Blocked_MM(int x, int y, int *goes_to_x, int *goes_to_y)
3751 int direction = MovDir[x][y];
3752 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3753 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3759 static void Blocked2Moving_MM(int x, int y,
3760 int *comes_from_x, int *comes_from_y)
3762 int oldx = x, oldy = y;
3763 int direction = MovDir[x][y];
3765 if (direction == MV_LEFT)
3767 else if (direction == MV_RIGHT)
3769 else if (direction == MV_UP)
3771 else if (direction == MV_DOWN)
3774 *comes_from_x = oldx;
3775 *comes_from_y = oldy;
3778 static int MovingOrBlocked2Element_MM(int x, int y)
3780 int element = Feld[x][y];
3782 if (element == EL_BLOCKED)
3786 Blocked2Moving_MM(x, y, &oldx, &oldy);
3788 return Feld[oldx][oldy];
3795 static void RemoveField(int x, int y)
3797 Feld[x][y] = EL_EMPTY;
3804 static void RemoveMovingField_MM(int x, int y)
3806 int oldx = x, oldy = y, newx = x, newy = y;
3808 if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
3811 if (IS_MOVING(x, y))
3813 Moving2Blocked_MM(x, y, &newx, &newy);
3814 if (Feld[newx][newy] != EL_BLOCKED)
3817 else if (Feld[x][y] == EL_BLOCKED)
3819 Blocked2Moving_MM(x, y, &oldx, &oldy);
3820 if (!IS_MOVING(oldx, oldy))
3824 Feld[oldx][oldy] = EL_EMPTY;
3825 Feld[newx][newy] = EL_EMPTY;
3826 MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
3827 MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
3829 DrawLevelField_MM(oldx, oldy);
3830 DrawLevelField_MM(newx, newy);
3833 void PlaySoundLevel(int x, int y, int sound_nr)
3835 int sx = SCREENX(x), sy = SCREENY(y);
3837 int silence_distance = 8;
3839 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
3840 (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
3843 if (!IN_LEV_FIELD(x, y) ||
3844 sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
3845 sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
3848 volume = SOUND_MAX_VOLUME;
3851 stereo = (sx - SCR_FIELDX/2) * 12;
3853 stereo = SOUND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
3854 if (stereo > SOUND_MAX_RIGHT)
3855 stereo = SOUND_MAX_RIGHT;
3856 if (stereo < SOUND_MAX_LEFT)
3857 stereo = SOUND_MAX_LEFT;
3860 if (!IN_SCR_FIELD(sx, sy))
3862 int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
3863 int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
3865 volume -= volume * (dx > dy ? dx : dy) / silence_distance;
3868 PlaySoundExt(sound_nr, volume, stereo, SND_CTRL_PLAY_SOUND);
3871 static void RaiseScore_MM(int value)
3873 game_mm.score += value;
3876 DrawText(DX_SCORE, DY_SCORE, int2str(game_mm.score, 4),
3881 void RaiseScoreElement_MM(int element)
3886 RaiseScore_MM(native_mm_level.score[SC_PACMAN]);
3890 RaiseScore_MM(native_mm_level.score[SC_KEY]);