1 // ============================================================================
2 // Mirror Magic -- McDuffin's Revenge
3 // ----------------------------------------------------------------------------
4 // (c) 1994-2017 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
18 /* graphic position values for game controls */
19 #define ENERGY_XSIZE 32
20 #define ENERGY_YSIZE MAX_LASER_ENERGY
21 #define OVERLOAD_XSIZE ENERGY_XSIZE
22 #define OVERLOAD_YSIZE MAX_LASER_OVERLOAD
24 /* values for Explode_MM() */
25 #define EX_PHASE_START 0
30 /* special positions in the game control window (relative to control window) */
39 #define XX_OVERLOAD 60
40 #define YY_OVERLOAD YY_ENERGY
42 /* special positions in the game control window (relative to main window) */
43 #define DX_LEVEL (DX + XX_LEVEL)
44 #define DY_LEVEL (DY + YY_LEVEL)
45 #define DX_KETTLES (DX + XX_KETTLES)
46 #define DY_KETTLES (DY + YY_KETTLES)
47 #define DX_SCORE (DX + XX_SCORE)
48 #define DY_SCORE (DY + YY_SCORE)
49 #define DX_ENERGY (DX + XX_ENERGY)
50 #define DY_ENERGY (DY + YY_ENERGY)
51 #define DX_OVERLOAD (DX + XX_OVERLOAD)
52 #define DY_OVERLOAD (DY + YY_OVERLOAD)
54 #define IS_LOOP_SOUND(s) ((s) == SND_FUEL)
55 #define IS_MUSIC_SOUND(s) ((s) == SND_TYGER || (s) == SND_VOYAGER)
57 /* game button identifiers */
58 #define GAME_CTRL_ID_LEFT 0
59 #define GAME_CTRL_ID_MIDDLE 1
60 #define GAME_CTRL_ID_RIGHT 2
62 #define NUM_GAME_BUTTONS 3
64 /* values for DrawLaser() */
65 #define DL_LASER_DISABLED 0
66 #define DL_LASER_ENABLED 1
68 /* values for 'click_delay_value' in ClickElement() */
69 #define CLICK_DELAY_SHORT 125
70 #define CLICK_DELAY_LONG 250
72 #define AUTO_ROTATE_DELAY CLICK_DELAY_SHORT
73 #define INIT_GAME_ACTIONS_DELAY ONE_SECOND_DELAY
74 #define NUM_INIT_CYCLE_STEPS 16
76 /* forward declaration for internal use */
77 static int MovingOrBlocked2Element_MM(int, int);
78 static void Bang_MM(int, int);
79 static void RaiseScore_MM(int);
80 static void RemoveMovingField_MM(int, int);
81 static void InitMovingField_MM(int, int, int);
82 static void ContinueMoving_MM(int, int);
83 static void Moving2Blocked_MM(int, int, int *, int *);
86 static int get_element_angle(int element)
88 int element_phase = get_element_phase(element);
90 if (IS_MIRROR_FIXED(element) ||
91 IS_MCDUFFIN(element) ||
94 return 4 * element_phase;
99 static int get_opposite_angle(int angle)
101 int opposite_angle = angle + ANG_RAY_180;
103 /* make sure "opposite_angle" is in valid interval [0, 15] */
104 return (opposite_angle + 16) % 16;
107 static int get_mirrored_angle(int laser_angle, int mirror_angle)
109 int reflected_angle = 16 - laser_angle + mirror_angle;
111 /* make sure "reflected_angle" is in valid interval [0, 15] */
112 return (reflected_angle + 16) % 16;
115 static void InitMovDir_MM(int x, int y)
117 int element = Feld[x][y];
118 static int direction[3][4] =
120 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
121 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
122 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
127 case EL_PACMAN_RIGHT:
131 Feld[x][y] = EL_PACMAN;
132 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
140 static void InitField(int x, int y, boolean init_game)
142 int element = Feld[x][y];
147 Feld[x][y] = EL_EMPTY;
152 if (native_mm_level.auto_count_kettles)
153 game_mm.kettles_still_needed++;
156 case EL_LIGHTBULB_OFF:
157 game_mm.lights_still_needed++;
161 if (IS_MIRROR(element) ||
162 IS_BEAMER_OLD(element) ||
163 IS_BEAMER(element) ||
165 IS_POLAR_CROSS(element) ||
166 IS_DF_MIRROR(element) ||
167 IS_DF_MIRROR_AUTO(element) ||
168 IS_GRID_STEEL_AUTO(element) ||
169 IS_GRID_WOOD_AUTO(element) ||
170 IS_FIBRE_OPTIC(element))
172 if (IS_BEAMER_OLD(element))
174 Feld[x][y] = EL_BEAMER_BLUE_START + (element - EL_BEAMER_START);
175 element = Feld[x][y];
178 if (!IS_FIBRE_OPTIC(element))
180 static int steps_grid_auto = 0;
182 if (game_mm.num_cycle == 0) /* initialize cycle steps for grids */
183 steps_grid_auto = RND(16) * (RND(2) ? -1 : +1);
185 if (IS_GRID_STEEL_AUTO(element) ||
186 IS_GRID_WOOD_AUTO(element))
187 game_mm.cycle[game_mm.num_cycle].steps = steps_grid_auto;
189 game_mm.cycle[game_mm.num_cycle].steps = RND(16) * (RND(2) ? -1 : +1);
191 game_mm.cycle[game_mm.num_cycle].x = x;
192 game_mm.cycle[game_mm.num_cycle].y = y;
196 if (IS_BEAMER(element) || IS_FIBRE_OPTIC(element))
198 int beamer_nr = BEAMER_NR(element);
199 int nr = laser.beamer[beamer_nr][0].num;
201 laser.beamer[beamer_nr][nr].x = x;
202 laser.beamer[beamer_nr][nr].y = y;
203 laser.beamer[beamer_nr][nr].num = 1;
206 else if (IS_PACMAN(element))
210 else if (IS_MCDUFFIN(element) || IS_LASER(element))
212 laser.start_edge.x = x;
213 laser.start_edge.y = y;
214 laser.start_angle = get_element_angle(element);
221 static void InitCycleElements_RotateSingleStep()
225 if (game_mm.num_cycle == 0) /* no elements to cycle */
228 for (i = 0; i < game_mm.num_cycle; i++)
230 int x = game_mm.cycle[i].x;
231 int y = game_mm.cycle[i].y;
232 int step = SIGN(game_mm.cycle[i].steps);
233 int last_element = Feld[x][y];
234 int next_element = get_rotated_element(last_element, step);
236 if (!game_mm.cycle[i].steps)
239 Feld[x][y] = next_element;
242 game_mm.cycle[i].steps -= step;
246 static void InitLaser()
248 int start_element = Feld[laser.start_edge.x][laser.start_edge.y];
249 int step = (IS_LASER(start_element) ? 4 : 0);
251 LX = laser.start_edge.x * TILEX;
252 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
255 LX += (laser.start_angle == ANG_RAY_RIGHT ? 28 + step : 0 - step);
257 LY = laser.start_edge.y * TILEY;
258 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
259 LY += (laser.start_angle == ANG_RAY_DOWN ? 28 + step : 0 - step);
263 XS = 2 * Step[laser.start_angle].x;
264 YS = 2 * Step[laser.start_angle].y;
266 laser.current_angle = laser.start_angle;
268 laser.num_damages = 0;
270 laser.num_beamers = 0;
271 laser.beamer_edge[0] = 0;
273 AddLaserEdge(LX, LY); /* set laser starting edge */
275 pen_ray = GetPixelFromRGB(window,
276 native_mm_level.laser_red * 0xFF,
277 native_mm_level.laser_green * 0xFF,
278 native_mm_level.laser_blue * 0xFF);
281 void InitGameEngine_MM()
285 /* set global game control values */
286 game_mm.num_cycle = 0;
287 game_mm.num_pacman = 0;
290 game_mm.energy_left = 0; // later set to "native_mm_level.time"
291 game_mm.kettles_still_needed =
292 (native_mm_level.auto_count_kettles ? 0 : native_mm_level.kettles_needed);
293 game_mm.lights_still_needed = 0;
294 game_mm.num_keys = 0;
296 game_mm.level_solved = FALSE;
297 game_mm.game_over = FALSE;
298 game_mm.game_over_cause = 0;
300 game_mm.laser_overload_value = 0;
302 /* set global laser control values (must be set before "InitLaser()") */
303 laser.start_edge.x = 0;
304 laser.start_edge.y = 0;
305 laser.start_angle = 0;
307 for (i = 0; i < MAX_NUM_BEAMERS; i++)
308 laser.beamer[i][0].num = laser.beamer[i][1].num = 0;
310 laser.overloaded = FALSE;
311 laser.overload_value = 0;
312 laser.fuse_off = FALSE;
313 laser.fuse_x = laser.fuse_y = -1;
315 laser.dest_element = EL_EMPTY;
320 for (x = 0; x < lev_fieldx; x++)
322 for (y = 0; y < lev_fieldy; y++)
324 Feld[x][y] = Ur[x][y];
325 Hit[x][y] = Box[x][y] = 0;
327 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
328 Store[x][y] = Store2[x][y] = 0;
332 InitField(x, y, TRUE);
337 CloseDoor(DOOR_CLOSE_1);
343 void InitGameActions_MM()
345 int num_init_game_frames = INIT_GAME_ACTIONS_DELAY / GAME_FRAME_DELAY;
346 int cycle_steps_done = 0;
352 /* copy default game door content to main double buffer */
353 BlitBitmap(pix[PIX_DOOR], drawto,
354 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
358 DrawText(DX_LEVEL, DY_LEVEL,
359 int2str(level_nr, 2), FONT_TEXT_2);
360 DrawText(DX_KETTLES, DY_KETTLES,
361 int2str(game_mm.kettles_still_needed, 3), FONT_TEXT_2);
362 DrawText(DX_SCORE, DY_SCORE,
363 int2str(game_mm.score, 4), FONT_TEXT_2);
372 /* copy actual game door content to door double buffer for OpenDoor() */
373 BlitBitmap(drawto, pix[PIX_DB_DOOR],
374 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
378 OpenDoor(DOOR_OPEN_ALL);
381 for (i = 0; i <= num_init_game_frames; i++)
383 if (i == num_init_game_frames)
384 StopSound_MM(SND_MM_GAME_LEVELTIME_CHARGING);
385 else if (setup.sound_loops)
386 PlaySoundLoop_MM(SND_MM_GAME_LEVELTIME_CHARGING);
388 PlaySound_MM(SND_MM_GAME_LEVELTIME_CHARGING);
390 game_mm.energy_left = native_mm_level.time * i / num_init_game_frames;
392 UpdateAndDisplayGameControlValues();
394 while (cycle_steps_done < NUM_INIT_CYCLE_STEPS * i / num_init_game_frames)
396 InitCycleElements_RotateSingleStep();
406 if (setup.quick_doors)
412 if (setup.sound_music && num_bg_loops)
413 PlayMusic(level_nr % num_bg_loops);
419 void AddLaserEdge(int lx, int ly)
421 if (lx < -2 || ly < -2 || lx >= SXSIZE + 2 || ly >= SYSIZE + 2)
423 Error(ERR_WARN, "AddLaserEdge: out of bounds: %d, %d", lx, ly);
428 laser.edge[laser.num_edges].x = SX + 2 + lx;
429 laser.edge[laser.num_edges].y = SY + 2 + ly;
435 void AddDamagedField(int ex, int ey)
437 laser.damage[laser.num_damages].is_mirror = FALSE;
438 laser.damage[laser.num_damages].angle = laser.current_angle;
439 laser.damage[laser.num_damages].edge = laser.num_edges;
440 laser.damage[laser.num_damages].x = ex;
441 laser.damage[laser.num_damages].y = ey;
451 int last_x = laser.edge[laser.num_edges - 1].x - SX - 2;
452 int last_y = laser.edge[laser.num_edges - 1].y - SY - 2;
454 return ((x - last_x) * XS < 0 || (y - last_y) * YS < 0);
460 static int getMaskFromElement(int element)
462 if (IS_GRID(element))
463 return IMG_MM_MASK_GRID_1 + get_element_phase(element);
464 else if (IS_MCDUFFIN(element))
465 return IMG_MM_MASK_MCDUFFIN_RIGHT + get_element_phase(element);
466 else if (IS_RECTANGLE(element) || IS_DF_GRID(element))
467 return IMG_MM_MASK_RECTANGLE;
469 return IMG_MM_MASK_CIRCLE;
477 printf("ScanPixel: start scanning at (%d, %d) [%d, %d] [%d, %d]\n",
478 LX, LY, LX / TILEX, LY / TILEY, LX % TILEX, LY % TILEY);
481 /* follow laser beam until it hits something (at least the screen border) */
482 while (hit_mask == HIT_MASK_NO_HIT)
488 if (SX + LX < REAL_SX || SX + LX >= REAL_SX + FULL_SXSIZE ||
489 SY + LY < REAL_SY || SY + LY >= REAL_SY + FULL_SYSIZE)
491 printf("ScanPixel: touched screen border!\n");
497 for (i = 0; i < 4; i++)
499 int px = LX + (i % 2) * 2;
500 int py = LY + (i / 2) * 2;
503 int lx = (px + TILEX) / TILEX - 1; /* ...+TILEX...-1 to get correct */
504 int ly = (py + TILEY) / TILEY - 1; /* negative values! */
507 if (IN_LEV_FIELD(lx, ly))
509 int element = Feld[lx][ly];
511 if (element == EL_EMPTY || element == EL_EXPLODING_TRANSP)
515 else if (IS_WALL(element) || IS_WALL_CHANGING(element))
517 int pos = dy / MINI_TILEY * 2 + dx / MINI_TILEX;
519 pixel = ((element & (1 << pos)) ? 1 : 0);
523 int graphic_mask = getMaskFromElement(element);
528 getGraphicSource(graphic_mask, 0, &bitmap, &src_x, &src_y);
533 pixel = (ReadPixel(bitmap, mask_x, mask_y) ? 1 : 0);
538 pixel = (SX + px < REAL_SX || SX + px >= REAL_SX + FULL_SXSIZE ||
539 SY + py < REAL_SY || SY + py >= REAL_SY + FULL_SYSIZE);
542 if ((Sign[laser.current_angle] & (1 << i)) && pixel)
543 hit_mask |= (1 << i);
546 if (hit_mask == HIT_MASK_NO_HIT)
548 /* hit nothing -- go on with another step */
560 int end = 0, rf = laser.num_edges;
562 /* do not scan laser again after the game was lost for whatever reason */
563 if (game_mm.game_over)
566 laser.overloaded = FALSE;
567 laser.stops_inside_element = FALSE;
569 DrawLaser(0, DL_LASER_ENABLED);
572 printf("Start scanning with LX == %d, LY == %d, XS == %d, YS == %d\n",
580 if (laser.num_edges > MAX_LASER_LEN || laser.num_damages > MAX_LASER_LEN)
583 laser.overloaded = TRUE;
588 hit_mask = ScanPixel();
591 printf("Hit something at LX == %d, LY == %d, XS == %d, YS == %d\n",
595 /* hit something -- check out what it was */
596 ELX = (LX + XS) / TILEX;
597 ELY = (LY + YS) / TILEY;
600 printf("hit_mask (1) == '%x' (%d, %d) (%d, %d)\n",
601 hit_mask, LX, LY, ELX, ELY);
604 if (!IN_LEV_FIELD(ELX, ELY) || !IN_PIX_FIELD(LX, LY))
607 laser.dest_element = element;
612 if (hit_mask == (HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT))
614 /* we have hit the top-right and bottom-left element --
615 choose the bottom-left one */
616 /* !!! THIS CAN BE DONE MORE INTELLIGENTLY, FOR EXAMPLE, IF ONE
617 ELEMENT WAS STEEL AND THE OTHER ONE WAS ICE => ALWAYS CHOOSE
618 THE ICE AND MELT IT AWAY INSTEAD OF OVERLOADING LASER !!! */
619 ELX = (LX - 2) / TILEX;
620 ELY = (LY + 2) / TILEY;
623 if (hit_mask == (HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT))
625 /* we have hit the top-left and bottom-right element --
626 choose the top-left one */
627 /* !!! SEE ABOVE !!! */
628 ELX = (LX - 2) / TILEX;
629 ELY = (LY - 2) / TILEY;
633 printf("hit_mask (2) == '%x' (%d, %d) (%d, %d)\n",
634 hit_mask, LX, LY, ELX, ELY);
637 element = Feld[ELX][ELY];
638 laser.dest_element = element;
641 printf("Hit element %d at (%d, %d) [%d, %d] [%d, %d] [%d]\n",
644 LX % TILEX, LY % TILEY,
649 if (!IN_LEV_FIELD(ELX, ELY))
650 printf("WARNING! (1) %d, %d (%d)\n", ELX, ELY, element);
653 if (element == EL_EMPTY)
655 if (!HitOnlyAnEdge(element, hit_mask))
658 else if (element == EL_FUSE_ON)
660 if (HitPolarizer(element, hit_mask))
663 else if (IS_GRID(element) || IS_DF_GRID(element))
665 if (HitPolarizer(element, hit_mask))
668 else if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD ||
669 element == EL_GATE_STONE || element == EL_GATE_WOOD)
671 if (HitBlock(element, hit_mask))
678 else if (IS_MCDUFFIN(element))
680 if (HitLaserSource(element, hit_mask))
683 else if ((element >= EL_EXIT_CLOSED && element <= EL_EXIT_OPEN) ||
684 IS_RECEIVER(element))
686 if (HitLaserDestination(element, hit_mask))
689 else if (IS_WALL(element))
691 if (IS_WALL_STEEL(element) || IS_DF_WALL_STEEL(element))
693 if (HitReflectingWalls(element, hit_mask))
698 if (HitAbsorbingWalls(element, hit_mask))
704 if (HitElement(element, hit_mask))
709 DrawLaser(rf - 1, DL_LASER_ENABLED);
710 rf = laser.num_edges;
714 if (laser.dest_element != Feld[ELX][ELY])
716 printf("ALARM: laser.dest_element == %d, Feld[ELX][ELY] == %d\n",
717 laser.dest_element, Feld[ELX][ELY]);
721 if (!end && !laser.stops_inside_element && !StepBehind())
724 printf("ScanLaser: Go one step back\n");
730 AddLaserEdge(LX, LY);
734 DrawLaser(rf - 1, DL_LASER_ENABLED);
739 if (!IN_LEV_FIELD(ELX, ELY))
740 printf("WARNING! (2) %d, %d\n", ELX, ELY);
744 void DrawLaserExt(int start_edge, int num_edges, int mode)
750 printf("DrawLaserExt: start_edge, num_edges, mode == %d, %d, %d\n",
751 start_edge, num_edges, mode);
756 Error(ERR_WARN, "DrawLaserExt: start_edge < 0");
763 Error(ERR_WARN, "DrawLaserExt: num_edges < 0");
769 if (mode == DL_LASER_DISABLED)
771 printf("DrawLaser: Delete laser from edge %d\n", start_edge);
775 /* now draw the laser to the backbuffer and (if enabled) to the screen */
776 DrawLines(drawto, &laser.edge[start_edge], num_edges,
777 (mode == DL_LASER_ENABLED ? pen_ray : pen_bg));
779 redraw_mask |= REDRAW_FIELD;
781 if (mode == DL_LASER_ENABLED)
784 /* after the laser was deleted, the "damaged" graphics must be restored */
785 if (laser.num_damages)
787 int damage_start = 0;
790 /* determine the starting edge, from which graphics need to be restored */
793 for (i = 0; i < laser.num_damages; i++)
795 if (laser.damage[i].edge == start_edge + 1)
804 /* restore graphics from this starting edge to the end of damage list */
805 for (i = damage_start; i < laser.num_damages; i++)
807 int lx = laser.damage[i].x;
808 int ly = laser.damage[i].y;
809 int element = Feld[lx][ly];
811 if (Hit[lx][ly] == laser.damage[i].edge)
812 if (!((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
815 if (Box[lx][ly] == laser.damage[i].edge)
818 if (IS_DRAWABLE(element))
819 DrawField_MM(lx, ly);
822 elx = laser.damage[damage_start].x;
823 ely = laser.damage[damage_start].y;
824 element = Feld[elx][ely];
827 if (IS_BEAMER(element))
831 for (i = 0; i < laser.num_beamers; i++)
832 printf("-> %d\n", laser.beamer_edge[i]);
833 printf("DrawLaserExt: IS_BEAMER: [%d]: Hit[%d][%d] == %d [%d]\n",
834 mode, elx, ely, Hit[elx][ely], start_edge);
835 printf("DrawLaserExt: IS_BEAMER: %d / %d\n",
836 get_element_angle(element), laser.damage[damage_start].angle);
840 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
841 laser.num_beamers > 0 &&
842 start_edge == laser.beamer_edge[laser.num_beamers - 1])
844 /* element is outgoing beamer */
845 laser.num_damages = damage_start + 1;
847 if (IS_BEAMER(element))
848 laser.current_angle = get_element_angle(element);
852 /* element is incoming beamer or other element */
853 laser.num_damages = damage_start;
854 laser.current_angle = laser.damage[laser.num_damages].angle;
859 /* no damages but McDuffin himself (who needs to be redrawn anyway) */
861 elx = laser.start_edge.x;
862 ely = laser.start_edge.y;
863 element = Feld[elx][ely];
866 laser.num_edges = start_edge + 1;
868 laser.current_angle = laser.start_angle;
870 LX = laser.edge[start_edge].x - (SX + 2);
871 LY = laser.edge[start_edge].y - (SY + 2);
872 XS = 2 * Step[laser.current_angle].x;
873 YS = 2 * Step[laser.current_angle].y;
876 printf("DrawLaser: Set (LX, LY) to (%d, %d) [%d]\n",
882 if (IS_BEAMER(element) ||
883 IS_FIBRE_OPTIC(element) ||
884 IS_PACMAN(element) ||
886 IS_POLAR_CROSS(element) ||
887 element == EL_FUSE_ON)
892 printf("element == %d\n", element);
895 if (IS_22_5_ANGLE(laser.current_angle)) /* neither 90° nor 45° angle */
896 step_size = ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) ? 4 : 3);
900 if (IS_POLAR(element) || IS_POLAR_CROSS(element) ||
901 ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
902 (laser.num_beamers == 0 ||
903 start_edge != laser.beamer_edge[laser.num_beamers - 1])))
905 /* element is incoming beamer or other element */
906 step_size = -step_size;
911 if (IS_BEAMER(element))
913 printf("start_edge == %d, laser.beamer_edge == %d\n",
914 start_edge, laser.beamer_edge);
918 LX += step_size * XS;
919 LY += step_size * YS;
921 else if (element != EL_EMPTY)
930 printf("DrawLaser: Finally: (LX, LY) to (%d, %d) [%d]\n",
935 void DrawLaser(int start_edge, int mode)
937 if (laser.num_edges - start_edge < 0)
939 Error(ERR_WARN, "DrawLaser: laser.num_edges - start_edge < 0");
944 /* check if laser is interrupted by beamer element */
945 if (laser.num_beamers > 0 &&
946 start_edge < laser.beamer_edge[laser.num_beamers - 1])
948 if (mode == DL_LASER_ENABLED)
951 int tmp_start_edge = start_edge;
953 /* draw laser segments forward from the start to the last beamer */
954 for (i = 0; i < laser.num_beamers; i++)
956 int tmp_num_edges = laser.beamer_edge[i] - tmp_start_edge;
958 if (tmp_num_edges <= 0)
962 printf("DrawLaser: DL_LASER_ENABLED: i==%d: %d, %d\n",
963 i, laser.beamer_edge[i], tmp_start_edge);
966 DrawLaserExt(tmp_start_edge, tmp_num_edges, DL_LASER_ENABLED);
968 tmp_start_edge = laser.beamer_edge[i];
971 /* draw last segment from last beamer to the end */
972 DrawLaserExt(tmp_start_edge, laser.num_edges - tmp_start_edge,
978 int last_num_edges = laser.num_edges;
979 int num_beamers = laser.num_beamers;
981 /* delete laser segments backward from the end to the first beamer */
982 for (i = num_beamers-1; i >= 0; i--)
984 int tmp_num_edges = last_num_edges - laser.beamer_edge[i];
986 if (laser.beamer_edge[i] - start_edge <= 0)
989 DrawLaserExt(laser.beamer_edge[i], tmp_num_edges, DL_LASER_DISABLED);
991 last_num_edges = laser.beamer_edge[i];
996 if (last_num_edges - start_edge <= 0)
997 printf("DrawLaser: DL_LASER_DISABLED: %d, %d\n",
998 last_num_edges, start_edge);
1001 /* delete first segment from start to the first beamer */
1002 DrawLaserExt(start_edge, last_num_edges - start_edge, DL_LASER_DISABLED);
1007 DrawLaserExt(start_edge, laser.num_edges - start_edge, mode);
1011 boolean HitElement(int element, int hit_mask)
1013 if (HitOnlyAnEdge(element, hit_mask))
1016 if (IS_MOVING(ELX, ELY) || IS_BLOCKED(ELX, ELY))
1017 element = MovingOrBlocked2Element_MM(ELX, ELY);
1020 printf("HitElement (1): element == %d\n", element);
1024 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1025 printf("HitElement (%d): EXACT MATCH @ (%d, %d)\n", element, ELX, ELY);
1027 printf("HitElement (%d): FUZZY MATCH @ (%d, %d)\n", element, ELX, ELY);
1030 AddDamagedField(ELX, ELY);
1032 /* this is more precise: check if laser would go through the center */
1033 if ((ELX * TILEX + 14 - LX) * YS != (ELY * TILEY + 14 - LY) * XS)
1035 /* skip the whole element before continuing the scan */
1041 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1043 if (LX/TILEX > ELX || LY/TILEY > ELY)
1045 /* skipping scan positions to the right and down skips one scan
1046 position too much, because this is only the top left scan position
1047 of totally four scan positions (plus one to the right, one to the
1048 bottom and one to the bottom right) */
1058 printf("HitElement (2): element == %d\n", element);
1061 if (LX + 5 * XS < 0 ||
1071 printf("HitElement (3): element == %d\n", element);
1074 if (IS_POLAR(element) &&
1075 ((element - EL_POLAR_START) % 2 ||
1076 (element - EL_POLAR_START) / 2 != laser.current_angle % 8))
1078 PlaySoundStereo(SND_KINK, ST(ELX));
1080 laser.num_damages--;
1085 if (IS_POLAR_CROSS(element) &&
1086 (element - EL_POLAR_CROSS_START) != laser.current_angle % 4)
1088 PlaySoundStereo(SND_KINK, ST(ELX));
1090 laser.num_damages--;
1095 if (!IS_BEAMER(element) &&
1096 !IS_FIBRE_OPTIC(element) &&
1097 !IS_GRID_WOOD(element) &&
1098 element != EL_FUEL_EMPTY)
1101 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1102 printf("EXACT MATCH @ (%d, %d)\n", ELX, ELY);
1104 printf("FUZZY MATCH @ (%d, %d)\n", ELX, ELY);
1107 LX = ELX * TILEX + 14;
1108 LY = ELY * TILEY + 14;
1110 AddLaserEdge(LX, LY);
1113 if (IS_MIRROR(element) ||
1114 IS_MIRROR_FIXED(element) ||
1115 IS_POLAR(element) ||
1116 IS_POLAR_CROSS(element) ||
1117 IS_DF_MIRROR(element) ||
1118 IS_DF_MIRROR_AUTO(element) ||
1119 element == EL_PRISM ||
1120 element == EL_REFRACTOR)
1122 int current_angle = laser.current_angle;
1125 laser.num_damages--;
1127 AddDamagedField(ELX, ELY);
1129 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1132 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1134 if (IS_MIRROR(element) ||
1135 IS_MIRROR_FIXED(element) ||
1136 IS_DF_MIRROR(element) ||
1137 IS_DF_MIRROR_AUTO(element))
1138 laser.current_angle = get_mirrored_angle(laser.current_angle,
1139 get_element_angle(element));
1141 if (element == EL_PRISM || element == EL_REFRACTOR)
1142 laser.current_angle = RND(16);
1144 XS = 2 * Step[laser.current_angle].x;
1145 YS = 2 * Step[laser.current_angle].y;
1147 if (!IS_22_5_ANGLE(laser.current_angle)) /* 90° or 45° angle */
1152 LX += step_size * XS;
1153 LY += step_size * YS;
1156 /* draw sparkles on mirror */
1157 if ((IS_MIRROR(element) || IS_MIRROR_FIXED(element)) &&
1158 current_angle != laser.current_angle)
1160 MoveSprite(vp, &Pfeil[2], 4 + 16 * ELX, 5 + 16 * ELY + 1);
1164 if ((!IS_POLAR(element) && !IS_POLAR_CROSS(element)) &&
1165 current_angle != laser.current_angle)
1166 PlaySoundStereo(SND_LASER, ST(ELX));
1169 (get_opposite_angle(laser.current_angle) ==
1170 laser.damage[laser.num_damages - 1].angle ? TRUE : FALSE);
1172 return (laser.overloaded ? TRUE : FALSE);
1175 if (element == EL_FUEL_FULL)
1177 laser.stops_inside_element = TRUE;
1182 if (element == EL_BOMB || element == EL_MINE)
1184 PlaySoundStereo(SND_KINK, ST(ELX));
1186 if (element == EL_MINE)
1187 laser.overloaded = TRUE;
1190 if (element == EL_KETTLE ||
1191 element == EL_CELL ||
1192 element == EL_KEY ||
1193 element == EL_LIGHTBALL ||
1194 element == EL_PACMAN ||
1197 if (!IS_PACMAN(element))
1200 if (element == EL_PACMAN)
1203 if (element == EL_KETTLE || element == EL_CELL)
1205 if (game_mm.kettles_still_needed > 0)
1206 game_mm.kettles_still_needed--;
1210 if (game_mm.kettles_still_needed == 0)
1213 static int xy[4][2] =
1221 PlaySoundStereo(SND_KLING, ST(ELX));
1223 for (y = 0; y < lev_fieldy; y++)
1225 for (x = 0; x < lev_fieldx; x++)
1227 /* initiate opening animation of exit door */
1228 if (Feld[x][y] == EL_EXIT_CLOSED)
1229 Feld[x][y] = EL_EXIT_OPENING;
1231 /* remove field that blocks receiver */
1232 if (IS_RECEIVER(Feld[x][y]))
1234 int phase = Feld[x][y] - EL_RECEIVER_START;
1235 int blocking_x, blocking_y;
1237 blocking_x = x + xy[phase][0];
1238 blocking_y = y + xy[phase][1];
1240 if (IN_LEV_FIELD(blocking_x, blocking_y))
1242 Feld[blocking_x][blocking_y] = EL_EMPTY;
1244 DrawField_MM(blocking_x, blocking_y);
1250 DrawLaser(0, DL_LASER_ENABLED);
1253 else if (element == EL_KEY)
1257 else if (element == EL_LIGHTBALL)
1261 else if (IS_PACMAN(element))
1263 DeletePacMan(ELX, ELY);
1270 if (element == EL_LIGHTBULB_OFF || element == EL_LIGHTBULB_ON)
1272 PlaySoundStereo(SND_KINK, ST(ELX));
1274 DrawLaser(0, DL_LASER_ENABLED);
1276 if (Feld[ELX][ELY] == EL_LIGHTBULB_OFF)
1278 Feld[ELX][ELY] = EL_LIGHTBULB_ON;
1279 game_mm.lights_still_needed--;
1283 Feld[ELX][ELY] = EL_LIGHTBULB_OFF;
1284 game_mm.lights_still_needed++;
1287 DrawField_MM(ELX, ELY);
1288 DrawLaser(0, DL_LASER_ENABLED);
1293 laser.stops_inside_element = TRUE;
1299 printf("HitElement (4): element == %d\n", element);
1302 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1303 laser.num_beamers < MAX_NUM_BEAMERS &&
1304 laser.beamer[BEAMER_NR(element)][1].num)
1306 int beamer_angle = get_element_angle(element);
1307 int beamer_nr = BEAMER_NR(element);
1311 printf("HitElement (BEAMER): element == %d\n", element);
1314 laser.num_damages--;
1316 if (IS_FIBRE_OPTIC(element) ||
1317 laser.current_angle == get_opposite_angle(beamer_angle))
1321 LX = ELX * TILEX + 14;
1322 LY = ELY * TILEY + 14;
1324 AddLaserEdge(LX, LY);
1325 AddDamagedField(ELX, ELY);
1327 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1330 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1332 pos = (ELX == laser.beamer[beamer_nr][0].x &&
1333 ELY == laser.beamer[beamer_nr][0].y ? 1 : 0);
1334 ELX = laser.beamer[beamer_nr][pos].x;
1335 ELY = laser.beamer[beamer_nr][pos].y;
1336 LX = ELX * TILEX + 14;
1337 LY = ELY * TILEY + 14;
1339 if (IS_BEAMER(element))
1341 laser.current_angle = get_element_angle(Feld[ELX][ELY]);
1342 XS = 2 * Step[laser.current_angle].x;
1343 YS = 2 * Step[laser.current_angle].y;
1346 laser.beamer_edge[laser.num_beamers] = laser.num_edges;
1348 AddLaserEdge(LX, LY);
1349 AddDamagedField(ELX, ELY);
1351 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1354 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1356 if (laser.current_angle == (laser.current_angle >> 1) << 1)
1361 LX += step_size * XS;
1362 LY += step_size * YS;
1364 laser.num_beamers++;
1373 boolean HitOnlyAnEdge(int element, int hit_mask)
1375 /* check if the laser hit only the edge of an element and, if so, go on */
1378 printf("LX, LY, hit_mask == %d, %d, %d\n", LX, LY, hit_mask);
1381 if ((hit_mask == HIT_MASK_TOPLEFT ||
1382 hit_mask == HIT_MASK_TOPRIGHT ||
1383 hit_mask == HIT_MASK_BOTTOMLEFT ||
1384 hit_mask == HIT_MASK_BOTTOMRIGHT) &&
1385 laser.current_angle % 4) /* angle is not 90° */
1389 if (hit_mask == HIT_MASK_TOPLEFT)
1394 else if (hit_mask == HIT_MASK_TOPRIGHT)
1399 else if (hit_mask == HIT_MASK_BOTTOMLEFT)
1404 else /* (hit_mask == HIT_MASK_BOTTOMRIGHT) */
1410 AddDamagedField((LX + 2 * dx) / TILEX, (LY + 2 * dy) / TILEY);
1416 printf("[HitOnlyAnEdge() == TRUE]\n");
1423 printf("[HitOnlyAnEdge() == FALSE]\n");
1429 boolean HitPolarizer(int element, int hit_mask)
1431 if (HitOnlyAnEdge(element, hit_mask))
1434 if (IS_DF_GRID(element))
1436 int grid_angle = get_element_angle(element);
1439 printf("HitPolarizer: angle: grid == %d, laser == %d\n",
1440 grid_angle, laser.current_angle);
1443 AddLaserEdge(LX, LY);
1444 AddDamagedField(ELX, ELY);
1447 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1449 if (laser.current_angle == grid_angle ||
1450 laser.current_angle == get_opposite_angle(grid_angle))
1452 /* skip the whole element before continuing the scan */
1458 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1460 if (LX/TILEX > ELX || LY/TILEY > ELY)
1462 /* skipping scan positions to the right and down skips one scan
1463 position too much, because this is only the top left scan position
1464 of totally four scan positions (plus one to the right, one to the
1465 bottom and one to the bottom right) */
1471 AddLaserEdge(LX, LY);
1477 printf("HitPolarizer: LX, LY == %d, %d [%d, %d] [%d, %d]\n",
1479 LX / TILEX, LY / TILEY,
1480 LX % TILEX, LY % TILEY);
1485 else if (IS_GRID_STEEL_FIXED(element) || IS_GRID_STEEL_AUTO(element))
1487 return HitReflectingWalls(element, hit_mask);
1491 return HitAbsorbingWalls(element, hit_mask);
1494 else if (IS_GRID_STEEL(element))
1496 return HitReflectingWalls(element, hit_mask);
1498 else /* IS_GRID_WOOD */
1500 return HitAbsorbingWalls(element, hit_mask);
1506 boolean HitBlock(int element, int hit_mask)
1508 boolean check = FALSE;
1510 if ((element == EL_GATE_STONE || element == EL_GATE_WOOD) &&
1511 game_mm.num_keys == 0)
1514 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1517 int ex = ELX * TILEX + 14;
1518 int ey = ELY * TILEY + 14;
1522 for (i = 1; i < 32; i++)
1527 if ((x == ex || x == ex + 1) && (y == ey || y == ey + 1))
1532 if (check && (element == EL_BLOCK_WOOD || element == EL_GATE_WOOD))
1533 return HitAbsorbingWalls(element, hit_mask);
1537 AddLaserEdge(LX - XS, LY - YS);
1538 AddDamagedField(ELX, ELY);
1541 Box[ELX][ELY] = laser.num_edges;
1543 return HitReflectingWalls(element, hit_mask);
1546 if (element == EL_GATE_STONE || element == EL_GATE_WOOD)
1548 int xs = XS / 2, ys = YS / 2;
1549 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1550 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1552 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1553 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1555 laser.overloaded = (element == EL_GATE_STONE);
1560 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1561 (hit_mask == HIT_MASK_TOP ||
1562 hit_mask == HIT_MASK_LEFT ||
1563 hit_mask == HIT_MASK_RIGHT ||
1564 hit_mask == HIT_MASK_BOTTOM))
1565 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1566 hit_mask == HIT_MASK_BOTTOM),
1567 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1568 hit_mask == HIT_MASK_RIGHT));
1569 AddLaserEdge(LX, LY);
1575 if (element == EL_GATE_STONE && Box[ELX][ELY])
1577 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
1589 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1591 int xs = XS / 2, ys = YS / 2;
1592 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1593 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1595 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1596 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1598 laser.overloaded = (element == EL_BLOCK_STONE);
1603 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1604 (hit_mask == HIT_MASK_TOP ||
1605 hit_mask == HIT_MASK_LEFT ||
1606 hit_mask == HIT_MASK_RIGHT ||
1607 hit_mask == HIT_MASK_BOTTOM))
1608 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1609 hit_mask == HIT_MASK_BOTTOM),
1610 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1611 hit_mask == HIT_MASK_RIGHT));
1612 AddDamagedField(ELX, ELY);
1614 LX = ELX * TILEX + 14;
1615 LY = ELY * TILEY + 14;
1617 AddLaserEdge(LX, LY);
1619 laser.stops_inside_element = TRUE;
1627 boolean HitLaserSource(int element, int hit_mask)
1629 if (HitOnlyAnEdge(element, hit_mask))
1632 PlaySoundStereo(SND_AUTSCH, ST(ELX));
1633 laser.overloaded = TRUE;
1638 boolean HitLaserDestination(int element, int hit_mask)
1640 if (HitOnlyAnEdge(element, hit_mask))
1643 if (element != EL_EXIT_OPEN &&
1644 !(IS_RECEIVER(element) &&
1645 game_mm.kettles_still_needed == 0 &&
1646 laser.current_angle == get_opposite_angle(get_element_angle(element))))
1648 PlaySoundStereo(SND_HOLZ, ST(ELX));
1652 if (IS_RECEIVER(element) ||
1653 (IS_22_5_ANGLE(laser.current_angle) &&
1654 (ELX != (LX + 6 * XS) / TILEX ||
1655 ELY != (LY + 6 * YS) / TILEY ||
1664 LX = ELX * TILEX + 14;
1665 LY = ELY * TILEY + 14;
1667 laser.stops_inside_element = TRUE;
1670 AddLaserEdge(LX, LY);
1671 AddDamagedField(ELX, ELY);
1673 if (game_mm.lights_still_needed == 0)
1674 game_mm.level_solved = TRUE;
1679 boolean HitReflectingWalls(int element, int hit_mask)
1681 /* check if laser hits side of a wall with an angle that is not 90° */
1682 if (!IS_90_ANGLE(laser.current_angle) && (hit_mask == HIT_MASK_TOP ||
1683 hit_mask == HIT_MASK_LEFT ||
1684 hit_mask == HIT_MASK_RIGHT ||
1685 hit_mask == HIT_MASK_BOTTOM))
1687 PlaySoundStereo(SND_HUI, ST(ELX));
1692 if (!IS_DF_GRID(element))
1693 AddLaserEdge(LX, LY);
1695 /* check if laser hits wall with an angle of 45° */
1696 if (!IS_22_5_ANGLE(laser.current_angle))
1698 if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1701 laser.current_angle = get_mirrored_angle(laser.current_angle,
1704 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1707 laser.current_angle = get_mirrored_angle(laser.current_angle,
1711 AddLaserEdge(LX, LY);
1713 XS = 2 * Step[laser.current_angle].x;
1714 YS = 2 * Step[laser.current_angle].y;
1718 else if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1720 laser.current_angle = get_mirrored_angle(laser.current_angle,
1725 if (!IS_DF_GRID(element))
1726 AddLaserEdge(LX, LY);
1731 if (!IS_DF_GRID(element))
1732 AddLaserEdge(LX, LY + YS / 2);
1735 if (!IS_DF_GRID(element))
1736 AddLaserEdge(LX, LY);
1739 YS = 2 * Step[laser.current_angle].y;
1743 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1745 laser.current_angle = get_mirrored_angle(laser.current_angle,
1750 if (!IS_DF_GRID(element))
1751 AddLaserEdge(LX, LY);
1756 if (!IS_DF_GRID(element))
1757 AddLaserEdge(LX + XS / 2, LY);
1760 if (!IS_DF_GRID(element))
1761 AddLaserEdge(LX, LY);
1764 XS = 2 * Step[laser.current_angle].x;
1770 /* reflection at the edge of reflecting DF style wall */
1771 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1773 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1774 hit_mask == HIT_MASK_TOPRIGHT) ||
1775 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1776 hit_mask == HIT_MASK_TOPLEFT) ||
1777 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1778 hit_mask == HIT_MASK_BOTTOMLEFT) ||
1779 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1780 hit_mask == HIT_MASK_BOTTOMRIGHT))
1783 (hit_mask == HIT_MASK_TOPRIGHT || hit_mask == HIT_MASK_BOTTOMLEFT ?
1784 ANG_MIRROR_135 : ANG_MIRROR_45);
1786 PlaySoundStereo(SND_HUI, ST(ELX));
1788 AddDamagedField(ELX, ELY);
1789 AddLaserEdge(LX, LY);
1791 laser.current_angle = get_mirrored_angle(laser.current_angle,
1799 AddLaserEdge(LX, LY);
1805 /* reflection inside an edge of reflecting DF style wall */
1806 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1808 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1809 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT)) ||
1810 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1811 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMRIGHT)) ||
1812 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1813 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT)) ||
1814 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1815 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPLEFT)))
1818 (hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT) ||
1819 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT) ?
1820 ANG_MIRROR_135 : ANG_MIRROR_45);
1822 PlaySoundStereo(SND_HUI, ST(ELX));
1825 AddDamagedField(ELX, ELY);
1828 AddLaserEdge(LX - XS, LY - YS);
1829 AddLaserEdge(LX - XS + (ABS(XS) == 4 ? XS/2 : 0),
1830 LY - YS + (ABS(YS) == 4 ? YS/2 : 0));
1832 laser.current_angle = get_mirrored_angle(laser.current_angle,
1840 AddLaserEdge(LX, LY);
1846 /* check if laser hits DF style wall with an angle of 90° */
1847 if (IS_DF_WALL(element) && IS_90_ANGLE(laser.current_angle))
1849 if ((IS_HORIZ_ANGLE(laser.current_angle) &&
1850 (!(hit_mask & HIT_MASK_TOP) || !(hit_mask & HIT_MASK_BOTTOM))) ||
1851 (IS_VERT_ANGLE(laser.current_angle) &&
1852 (!(hit_mask & HIT_MASK_LEFT) || !(hit_mask & HIT_MASK_RIGHT))))
1854 static int last_LX = 0, last_LY = 0, last_hit_mask = 0;
1856 /* laser at last step touched nothing or the same side of the wall */
1857 if (LX != last_LX || LY != last_LY || hit_mask == last_hit_mask)
1859 AddDamagedField(ELX, ELY);
1866 last_hit_mask = hit_mask;
1873 if (!HitOnlyAnEdge(element, hit_mask))
1875 laser.overloaded = TRUE;
1883 boolean HitAbsorbingWalls(int element, int hit_mask)
1885 if (HitOnlyAnEdge(element, hit_mask))
1889 (hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT))
1891 AddLaserEdge(LX - XS, LY - YS);
1898 (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM))
1900 AddLaserEdge(LX - XS, LY - YS);
1906 if (IS_WALL_WOOD(element) ||
1907 IS_DF_WALL_WOOD(element) ||
1908 IS_GRID_WOOD(element) ||
1909 IS_GRID_WOOD_FIXED(element) ||
1910 IS_GRID_WOOD_AUTO(element) ||
1911 element == EL_FUSE_ON ||
1912 element == EL_BLOCK_WOOD ||
1913 element == EL_GATE_WOOD)
1915 PlaySoundStereo(SND_HOLZ, ST(ELX));
1920 if (IS_WALL_ICE(element))
1924 mask = (LX + XS) / MINI_TILEX - ELX * 2 + 1; /* Quadrant (horizontal) */
1925 mask <<= (((LY + YS) / MINI_TILEY - ELY * 2) > 0) * 2; /* || (vertical) */
1927 /* check if laser hits wall with an angle of 90° */
1928 if (IS_90_ANGLE(laser.current_angle))
1929 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1931 if (mask == 1 || mask == 2 || mask == 4 || mask == 8)
1935 for (i = 0; i < 4; i++)
1937 if (mask == (1 << i) && (XS > 0) == (i % 2) && (YS > 0) == (i / 2))
1938 mask = 15 - (8 >> i);
1939 else if (ABS(XS) == 4 &&
1941 (XS > 0) == (i % 2) &&
1942 (YS < 0) == (i / 2))
1943 mask = 3 + (i / 2) * 9;
1944 else if (ABS(YS) == 4 &&
1946 (XS < 0) == (i % 2) &&
1947 (YS > 0) == (i / 2))
1948 mask = 5 + (i % 2) * 5;
1952 laser.wall_mask = mask;
1954 else if (IS_WALL_AMOEBA(element))
1956 int elx = (LX - 2 * XS) / TILEX;
1957 int ely = (LY - 2 * YS) / TILEY;
1958 int element2 = Feld[elx][ely];
1961 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element2))
1963 laser.dest_element = EL_EMPTY;
1971 mask = (LX - 2 * XS) / 16 - ELX * 2 + 1;
1972 mask <<= ((LY - 2 * YS) / 16 - ELY * 2) * 2;
1974 if (IS_90_ANGLE(laser.current_angle))
1975 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1977 laser.dest_element = element2 | EL_WALL_AMOEBA;
1979 laser.wall_mask = mask;
1985 void OpenExit(int x, int y)
1989 if (!MovDelay[x][y]) /* next animation frame */
1990 MovDelay[x][y] = 4 * delay;
1992 if (MovDelay[x][y]) /* wait some time before next frame */
1997 phase = MovDelay[x][y] / delay;
1999 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2000 DrawGraphicAnimation_MM(x, y, IMG_MM_EXIT_OPENING, 3 - phase);
2002 if (!MovDelay[x][y])
2004 Feld[x][y] = EL_EXIT_OPEN;
2010 void OpenSurpriseBall(int x, int y)
2014 if (!MovDelay[x][y]) /* next animation frame */
2015 MovDelay[x][y] = 50 * delay;
2017 if (MovDelay[x][y]) /* wait some time before next frame */
2021 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2024 int graphic = el2gfx(Store[x][y]);
2026 int dx = RND(26), dy = RND(26);
2028 getGraphicSource(graphic, 0, &bitmap, &gx, &gy);
2030 BlitBitmap(bitmap, drawto, gx + dx, gy + dy, 6, 6,
2031 SX + x * TILEX + dx, SY + y * TILEY + dy);
2033 MarkTileDirty(x, y);
2036 if (!MovDelay[x][y])
2038 Feld[x][y] = Store[x][y];
2047 void MeltIce(int x, int y)
2052 if (!MovDelay[x][y]) /* next animation frame */
2053 MovDelay[x][y] = frames * delay;
2055 if (MovDelay[x][y]) /* wait some time before next frame */
2058 int wall_mask = Store2[x][y];
2059 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_ICE;
2062 phase = frames - MovDelay[x][y] / delay - 1;
2064 if (!MovDelay[x][y])
2068 Feld[x][y] = real_element & (wall_mask ^ 0xFF);
2069 Store[x][y] = Store2[x][y] = 0;
2071 DrawWalls_MM(x, y, Feld[x][y]);
2073 if (Feld[x][y] == EL_WALL_ICE)
2074 Feld[x][y] = EL_EMPTY;
2076 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
2077 if (laser.damage[i].is_mirror)
2081 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
2083 DrawLaser(0, DL_LASER_DISABLED);
2087 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2089 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2091 laser.redraw = TRUE;
2096 void GrowAmoeba(int x, int y)
2101 if (!MovDelay[x][y]) /* next animation frame */
2102 MovDelay[x][y] = frames * delay;
2104 if (MovDelay[x][y]) /* wait some time before next frame */
2107 int wall_mask = Store2[x][y];
2108 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_AMOEBA;
2111 phase = MovDelay[x][y] / delay;
2113 if (!MovDelay[x][y])
2115 Feld[x][y] = real_element;
2116 Store[x][y] = Store2[x][y] = 0;
2118 DrawWalls_MM(x, y, Feld[x][y]);
2119 DrawLaser(0, DL_LASER_ENABLED);
2121 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2123 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2128 static void Explode_MM(int x, int y, int phase, int mode)
2130 int num_phase = 9, delay = 2;
2131 int last_phase = num_phase * delay;
2132 int half_phase = (num_phase / 2) * delay;
2134 laser.redraw = TRUE;
2136 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2138 int center_element = Feld[x][y];
2140 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2142 /* put moving element to center field (and let it explode there) */
2143 center_element = MovingOrBlocked2Element_MM(x, y);
2144 RemoveMovingField_MM(x, y);
2146 Feld[x][y] = center_element;
2149 if (center_element == EL_BOMB || IS_MCDUFFIN(center_element))
2150 Store[x][y] = center_element;
2152 Store[x][y] = EL_EMPTY;
2154 Store2[x][y] = mode;
2155 Feld[x][y] = EL_EXPLODING_OPAQUE;
2156 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2162 Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
2164 if (phase == half_phase)
2166 Feld[x][y] = EL_EXPLODING_TRANSP;
2168 if (x == ELX && y == ELY)
2172 if (phase == last_phase)
2174 if (Store[x][y] == EL_BOMB)
2176 laser.num_damages--;
2177 DrawLaser(0, DL_LASER_DISABLED);
2178 laser.num_edges = 0;
2180 Bang_MM(laser.start_edge.x, laser.start_edge.y);
2181 Store[x][y] = EL_EMPTY;
2183 else if (IS_MCDUFFIN(Store[x][y]))
2185 game_mm.game_over = TRUE;
2186 game_mm.game_over_cause = GAME_OVER_BOMB;
2187 Store[x][y] = EL_EMPTY;
2190 Feld[x][y] = Store[x][y];
2191 Store[x][y] = Store2[x][y] = 0;
2192 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2194 InitField(x, y, FALSE);
2197 else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2199 int graphic = IMG_MM_DEFAULT_EXPLODING;
2200 int graphic_phase = (phase / delay - 1);
2204 if (Store2[x][y] == EX_KETTLE)
2206 if (graphic_phase < 3)
2208 graphic = IMG_MM_KETTLE_EXPLODING;
2210 else if (graphic_phase < 5)
2216 graphic = IMG_EMPTY;
2220 else if (Store2[x][y] == EX_SHORT)
2222 if (graphic_phase < 4)
2228 graphic = IMG_EMPTY;
2233 getGraphicSource(graphic, graphic_phase, &bitmap, &src_x, &src_y);
2235 BlitBitmap(bitmap, drawto_field, src_x, src_y, TILEX, TILEY,
2236 FX + x * TILEX, FY + y * TILEY);
2238 MarkTileDirty(x, y);
2242 static void Bang_MM(int x, int y)
2244 int element = Feld[x][y];
2245 int mode = EX_NORMAL;
2248 DrawLaser(0, DL_LASER_ENABLED);
2267 if (IS_PACMAN(element))
2268 PlaySoundStereo(SND_QUIEK, ST(x));
2269 else if (element == EL_BOMB || IS_MCDUFFIN(element))
2270 PlaySoundStereo(SND_ROAAAR, ST(x));
2271 else if (element == EL_KEY)
2272 PlaySoundStereo(SND_KLING, ST(x));
2274 PlaySoundStereo((mode == EX_SHORT ? SND_WHOOSH : SND_KABUMM), ST(x));
2276 Explode_MM(x, y, EX_PHASE_START, mode);
2279 void TurnRound(int x, int y)
2291 { 0, 0 }, { 0, 0 }, { 0, 0 },
2296 int left, right, back;
2300 { MV_DOWN, MV_UP, MV_RIGHT },
2301 { MV_UP, MV_DOWN, MV_LEFT },
2303 { MV_LEFT, MV_RIGHT, MV_DOWN },
2304 { 0,0,0 }, { 0,0,0 }, { 0,0,0 },
2305 { MV_RIGHT, MV_LEFT, MV_UP }
2308 int element = Feld[x][y];
2309 int old_move_dir = MovDir[x][y];
2310 int right_dir = turn[old_move_dir].right;
2311 int back_dir = turn[old_move_dir].back;
2312 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
2313 int right_x = x + right_dx, right_y = y + right_dy;
2315 if (element == EL_PACMAN)
2317 boolean can_turn_right = FALSE;
2319 if (IN_LEV_FIELD(right_x, right_y) &&
2320 IS_EATABLE4PACMAN(Feld[right_x][right_y]))
2321 can_turn_right = TRUE;
2324 MovDir[x][y] = right_dir;
2326 MovDir[x][y] = back_dir;
2332 static void StartMoving_MM(int x, int y)
2334 int element = Feld[x][y];
2339 if (CAN_MOVE(element))
2343 if (MovDelay[x][y]) /* wait some time before next movement */
2351 /* now make next step */
2353 Moving2Blocked_MM(x, y, &newx, &newy); /* get next screen position */
2355 if (element == EL_PACMAN &&
2356 IN_LEV_FIELD(newx, newy) && IS_EATABLE4PACMAN(Feld[newx][newy]) &&
2357 !ObjHit(newx, newy, HIT_POS_CENTER))
2359 Store[newx][newy] = Feld[newx][newy];
2360 Feld[newx][newy] = EL_EMPTY;
2362 DrawField_MM(newx, newy);
2364 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy) ||
2365 ObjHit(newx, newy, HIT_POS_CENTER))
2367 /* object was running against a wall */
2374 InitMovingField_MM(x, y, MovDir[x][y]);
2378 ContinueMoving_MM(x, y);
2381 static void ContinueMoving_MM(int x, int y)
2383 int element = Feld[x][y];
2384 int direction = MovDir[x][y];
2385 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2386 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2387 int horiz_move = (dx!=0);
2388 int newx = x + dx, newy = y + dy;
2389 int step = (horiz_move ? dx : dy) * TILEX / 8;
2391 MovPos[x][y] += step;
2393 if (ABS(MovPos[x][y]) >= TILEX) /* object reached its destination */
2395 Feld[x][y] = EL_EMPTY;
2396 Feld[newx][newy] = element;
2398 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2399 MovDelay[newx][newy] = 0;
2401 if (!CAN_MOVE(element))
2402 MovDir[newx][newy] = 0;
2405 DrawField_MM(newx, newy);
2407 Stop[newx][newy] = TRUE;
2409 if (element == EL_PACMAN)
2411 if (Store[newx][newy] == EL_BOMB)
2412 Bang_MM(newx, newy);
2414 if (IS_WALL_AMOEBA(Store[newx][newy]) &&
2415 (LX + 2 * XS) / TILEX == newx &&
2416 (LY + 2 * YS) / TILEY == newy)
2423 else /* still moving on */
2428 laser.redraw = TRUE;
2431 void ClickElement(int x, int y, int button)
2433 static unsigned int click_delay = 0;
2434 static int click_delay_value = CLICK_DELAY_SHORT;
2435 static boolean new_button = TRUE;
2438 /* do not rotate objects hit by the laser after the game was solved */
2439 if (game_mm.level_solved && Hit[x][y])
2442 if (button == MB_RELEASED)
2445 click_delay_value = CLICK_DELAY_SHORT;
2447 /* release eventually hold auto-rotating mirror */
2448 RotateMirror(x, y, MB_RELEASED);
2453 if (!DelayReached(&click_delay, click_delay_value) && !new_button)
2456 if (button == MB_MIDDLEBUTTON) /* middle button has no function */
2459 if (!IN_LEV_FIELD(x, y))
2462 if (Feld[x][y] == EL_EMPTY)
2465 element = Feld[x][y];
2467 if (IS_MIRROR(element) ||
2468 IS_BEAMER(element) ||
2469 IS_POLAR(element) ||
2470 IS_POLAR_CROSS(element) ||
2471 IS_DF_MIRROR(element) ||
2472 IS_DF_MIRROR_AUTO(element))
2474 RotateMirror(x, y, button);
2476 else if (IS_MCDUFFIN(element))
2478 if (!laser.fuse_off)
2480 DrawLaser(0, DL_LASER_DISABLED);
2487 element = get_rotated_element(element, BUTTON_ROTATION(button));
2488 laser.start_angle = get_element_angle(element);
2492 Feld[x][y] = element;
2499 if (!laser.fuse_off)
2502 else if (element == EL_FUSE_ON && laser.fuse_off)
2504 if (x != laser.fuse_x || y != laser.fuse_y)
2507 laser.fuse_off = FALSE;
2508 laser.fuse_x = laser.fuse_y = -1;
2510 DrawGraphic_MM(x, y, IMG_MM_FUSE_ACTIVE);
2513 else if (element == EL_FUSE_ON && !laser.fuse_off && new_button)
2515 laser.fuse_off = TRUE;
2518 laser.overloaded = FALSE;
2520 DrawLaser(0, DL_LASER_DISABLED);
2521 DrawGraphic_MM(x, y, IMG_MM_FUSE);
2523 else if (element == EL_LIGHTBALL)
2527 DrawLaser(0, DL_LASER_ENABLED);
2530 click_delay_value = (new_button ? CLICK_DELAY_LONG : CLICK_DELAY_SHORT);
2534 void RotateMirror(int x, int y, int button)
2536 static int hold_x = -1, hold_y = -1;
2538 if (button == MB_RELEASED)
2540 /* release eventually hold auto-rotating mirror */
2547 if (IS_MIRROR(Feld[x][y]) ||
2548 IS_POLAR_CROSS(Feld[x][y]) ||
2549 IS_POLAR(Feld[x][y]) ||
2550 IS_BEAMER(Feld[x][y]) ||
2551 IS_DF_MIRROR(Feld[x][y]) ||
2552 IS_GRID_STEEL_AUTO(Feld[x][y]) ||
2553 IS_GRID_WOOD_AUTO(Feld[x][y]))
2555 Feld[x][y] = get_rotated_element(Feld[x][y], BUTTON_ROTATION(button));
2557 else if (IS_DF_MIRROR_AUTO(Feld[x][y]))
2559 if (button == MB_LEFTBUTTON)
2561 /* left mouse button only for manual adjustment, no auto-rotating;
2562 freeze mirror for until mouse button released */
2566 else if (button == MB_RIGHTBUTTON && (hold_x != x || hold_y != y))
2568 Feld[x][y] = get_rotated_element(Feld[x][y], ROTATE_RIGHT);
2572 if (IS_GRID_STEEL_AUTO(Feld[x][y]) || IS_GRID_WOOD_AUTO(Feld[x][y]))
2574 int edge = Hit[x][y];
2580 DrawLaser(edge - 1, DL_LASER_DISABLED);
2584 else if (ObjHit(x, y, HIT_POS_CENTER))
2586 int edge = Hit[x][y];
2590 Error(ERR_WARN, "RotateMirror: inconsistent field Hit[][]!\n");
2594 DrawLaser(edge - 1, DL_LASER_DISABLED);
2601 if (ObjHit(x, y, HIT_POS_EDGE | HIT_POS_BETWEEN))
2606 if ((IS_BEAMER(Feld[x][y]) ||
2607 IS_POLAR(Feld[x][y]) ||
2608 IS_POLAR_CROSS(Feld[x][y])) && x == ELX && y == ELY)
2612 if (IS_BEAMER(Feld[x][y]))
2615 printf("TEST (%d, %d) [%d] [%d]\n",
2617 laser.beamer_edge, laser.beamer[1].num);
2627 DrawLaser(0, DL_LASER_ENABLED);
2631 void AutoRotateMirrors()
2633 static unsigned int rotate_delay = 0;
2636 if (!DelayReached(&rotate_delay, AUTO_ROTATE_DELAY))
2639 for (x = 0; x < lev_fieldx; x++)
2641 for (y = 0; y < lev_fieldy; y++)
2643 int element = Feld[x][y];
2645 /* do not rotate objects hit by the laser after the game was solved */
2646 if (game_mm.level_solved && Hit[x][y])
2649 if (IS_DF_MIRROR_AUTO(element) ||
2650 IS_GRID_WOOD_AUTO(element) ||
2651 IS_GRID_STEEL_AUTO(element) ||
2652 element == EL_REFRACTOR)
2653 RotateMirror(x, y, MB_RIGHTBUTTON);
2658 boolean ObjHit(int obx, int oby, int bits)
2665 if (bits & HIT_POS_CENTER)
2667 if (ReadPixel(drawto, SX + obx + 15, SY + oby + 15) == pen_ray)
2671 if (bits & HIT_POS_EDGE)
2673 for (i = 0; i < 4; i++)
2674 if (ReadPixel(drawto,
2675 SX + obx + 31 * (i % 2),
2676 SY + oby + 31 * (i / 2)) == pen_ray)
2680 if (bits & HIT_POS_BETWEEN)
2682 for (i = 0; i < 4; i++)
2683 if (ReadPixel(drawto,
2684 SX + 4 + obx + 22 * (i % 2),
2685 SY + 4 + oby + 22 * (i / 2)) == pen_ray)
2692 void DeletePacMan(int px, int py)
2698 if (game_mm.num_pacman <= 1)
2700 game_mm.num_pacman = 0;
2704 for (i = 0; i < game_mm.num_pacman; i++)
2705 if (game_mm.pacman[i].x == px && game_mm.pacman[i].y == py)
2708 game_mm.num_pacman--;
2710 for (j = i; j < game_mm.num_pacman; j++)
2712 game_mm.pacman[j].x = game_mm.pacman[j + 1].x;
2713 game_mm.pacman[j].y = game_mm.pacman[j + 1].y;
2714 game_mm.pacman[j].dir = game_mm.pacman[j + 1].dir;
2718 void ColorCycling(void)
2720 static int CC, Cc = 0;
2722 static int color, old = 0xF00, new = 0x010, mult = 1;
2723 static unsigned short red, green, blue;
2725 if (color_status == STATIC_COLORS)
2730 if (CC < Cc || CC > Cc + 50)
2734 color = old + new * mult;
2740 if (ABS(mult) == 16)
2750 red = 0x0e00 * ((color & 0xF00) >> 8);
2751 green = 0x0e00 * ((color & 0x0F0) >> 4);
2752 blue = 0x0e00 * ((color & 0x00F));
2753 SetRGB(pen_magicolor[0], red, green, blue);
2755 red = 0x1111 * ((color & 0xF00) >> 8);
2756 green = 0x1111 * ((color & 0x0F0) >> 4);
2757 blue = 0x1111 * ((color & 0x00F));
2758 SetRGB(pen_magicolor[1], red, green, blue);
2762 static void GameActions_MM_Ext(struct MouseActionInfo action, boolean warp_mode)
2764 static unsigned int action_delay = 0;
2765 static unsigned int pacman_delay = 0;
2766 static unsigned int energy_delay = 0;
2767 static unsigned int overload_delay = 0;
2773 WaitUntilDelayReached(&action_delay, GameFrameDelay);
2775 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2778 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2780 element = Feld[x][y];
2782 if (!IS_MOVING(x, y) && CAN_MOVE(element))
2783 StartMoving_MM(x, y);
2784 else if (IS_MOVING(x, y))
2785 ContinueMoving_MM(x, y);
2786 else if (IS_EXPLODING(element))
2787 Explode_MM(x, y, Frame[x][y], EX_NORMAL);
2788 else if (element == EL_EXIT_OPENING)
2790 else if (element == EL_GRAY_BALL_OPENING)
2791 OpenSurpriseBall(x, y);
2792 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_ICE)
2794 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_AMOEBA)
2798 AutoRotateMirrors();
2801 /* !!! CHANGE THIS: REDRAW ONLY WHEN NEEDED !!! */
2803 /* redraw after Explode_MM() ... */
2805 DrawLaser(0, DL_LASER_ENABLED);
2806 laser.redraw = FALSE;
2811 if (game_mm.num_pacman && DelayReached(&pacman_delay, 250))
2815 if (laser.num_damages > MAX_LASER_LEN && !laser.fuse_off)
2817 DrawLaser(0, DL_LASER_DISABLED);
2822 if (DelayReached(&energy_delay, 4000))
2824 game_mm.energy_left--;
2825 if (game_mm.energy_left >= 0)
2828 BlitBitmap(pix[PIX_DOOR], drawto,
2829 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
2830 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
2831 DX_ENERGY, DY_ENERGY);
2833 redraw_mask |= REDRAW_DOOR_1;
2835 else if (setup.time_limit)
2839 for (i = 15; i >= 0; i--)
2842 SetRGB(pen_ray, 0x0000, 0x0000, i * color_scale);
2844 pen_ray = GetPixelFromRGB(window,
2845 native_mm_level.laser_red * 0x11 * i,
2846 native_mm_level.laser_green * 0x11 * i,
2847 native_mm_level.laser_blue * 0x11 * i);
2849 DrawLaser(0, DL_LASER_ENABLED);
2854 StopSound(SND_WARNTON);
2857 DrawLaser(0, DL_LASER_DISABLED);
2858 game_mm.game_over = TRUE;
2859 game_mm.game_over_cause = GAME_OVER_NO_ENERGY;
2862 if (Request("Out of magic energy ! Play it again ?",
2863 REQ_ASK | REQ_STAY_CLOSED))
2869 game_status = MAINMENU;
2878 element = laser.dest_element;
2881 if (element != Feld[ELX][ELY])
2883 printf("element == %d, Feld[ELX][ELY] == %d\n",
2884 element, Feld[ELX][ELY]);
2888 if (!laser.overloaded && laser.overload_value == 0 &&
2889 element != EL_BOMB &&
2890 element != EL_MINE &&
2891 element != EL_BALL_GRAY &&
2892 element != EL_BLOCK_STONE &&
2893 element != EL_BLOCK_WOOD &&
2894 element != EL_FUSE_ON &&
2895 element != EL_FUEL_FULL &&
2896 !IS_WALL_ICE(element) &&
2897 !IS_WALL_AMOEBA(element))
2900 if (((laser.overloaded && laser.overload_value < MAX_LASER_OVERLOAD) ||
2901 (!laser.overloaded && laser.overload_value > 0)) &&
2902 DelayReached(&overload_delay, 60 + !laser.overloaded * 120))
2904 if (laser.overloaded)
2905 laser.overload_value++;
2907 laser.overload_value--;
2909 if (game_mm.cheat_no_overload)
2911 laser.overloaded = FALSE;
2912 laser.overload_value = 0;
2915 game_mm.laser_overload_value = laser.overload_value;
2917 if (laser.overload_value < MAX_LASER_OVERLOAD - 8)
2919 int color_up = 0xFF * laser.overload_value / MAX_LASER_OVERLOAD;
2920 int color_down = 0xFF - color_up;
2923 SetRGB(pen_ray, (laser.overload_value / 6) * color_scale, 0x0000,
2924 (15 - (laser.overload_value / 6)) * color_scale);
2927 GetPixelFromRGB(window,
2928 (native_mm_level.laser_red ? 0xFF : color_up),
2929 (native_mm_level.laser_green ? color_down : 0x00),
2930 (native_mm_level.laser_blue ? color_down : 0x00));
2932 DrawLaser(0, DL_LASER_ENABLED);
2936 if (laser.overloaded)
2938 if (setup.sound_loops)
2939 PlaySoundExt(SND_WARNTON, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
2940 SND_CTRL_PLAY_LOOP);
2942 PlaySoundStereo(SND_WARNTON, SOUND_MAX_RIGHT);
2945 if (!laser.overloaded)
2946 StopSound(SND_WARNTON);
2948 if (laser.overloaded)
2951 BlitBitmap(pix[PIX_DOOR], drawto,
2952 DOOR_GFX_PAGEX4 + XX_OVERLOAD,
2953 DOOR_GFX_PAGEY1 + YY_OVERLOAD + OVERLOAD_YSIZE
2954 - laser.overload_value,
2955 OVERLOAD_XSIZE, laser.overload_value,
2956 DX_OVERLOAD, DY_OVERLOAD + OVERLOAD_YSIZE
2957 - laser.overload_value);
2959 redraw_mask |= REDRAW_DOOR_1;
2964 BlitBitmap(pix[PIX_DOOR], drawto,
2965 DOOR_GFX_PAGEX5 + XX_OVERLOAD, DOOR_GFX_PAGEY1 + YY_OVERLOAD,
2966 OVERLOAD_XSIZE, OVERLOAD_YSIZE - laser.overload_value,
2967 DX_OVERLOAD, DY_OVERLOAD);
2969 redraw_mask |= REDRAW_DOOR_1;
2972 if (laser.overload_value == MAX_LASER_OVERLOAD)
2976 for (i = 15; i >= 0; i--)
2979 SetRGB(pen_ray, i * color_scale, 0x0000, 0x0000);
2982 pen_ray = GetPixelFromRGB(window, 0x11 * i, 0x00, 0x00);
2984 DrawLaser(0, DL_LASER_ENABLED);
2989 DrawLaser(0, DL_LASER_DISABLED);
2991 game_mm.game_over = TRUE;
2992 game_mm.game_over_cause = GAME_OVER_OVERLOADED;
2995 if (Request("Magic spell hit Mc Duffin ! Play it again ?",
2996 REQ_ASK | REQ_STAY_CLOSED))
3002 game_status = MAINMENU;
3016 if (element == EL_BOMB && CT > 1500)
3018 if (game_mm.cheat_no_explosion)
3022 laser.num_damages--;
3023 DrawLaser(0, DL_LASER_DISABLED);
3024 laser.num_edges = 0;
3029 laser.dest_element = EL_EXPLODING_OPAQUE;
3033 laser.num_damages--;
3034 DrawLaser(0, DL_LASER_DISABLED);
3036 laser.num_edges = 0;
3037 Bang_MM(laser.start_edge.x, laser.start_edge.y);
3039 if (Request("Bomb killed Mc Duffin ! Play it again ?",
3040 REQ_ASK | REQ_STAY_CLOSED))
3046 game_status = MAINMENU;
3054 if (element == EL_FUSE_ON && CT > 500)
3056 laser.fuse_off = TRUE;
3060 DrawLaser(0, DL_LASER_DISABLED);
3061 DrawGraphic_MM(ELX, ELY, IMG_MM_FUSE);
3064 if (element == EL_BALL_GRAY && CT > 1500)
3066 static int new_elements[] =
3069 EL_MIRROR_FIXED_START,
3071 EL_POLAR_CROSS_START,
3077 int num_new_elements = sizeof(new_elements) / sizeof(int);
3078 int new_element = new_elements[RND(num_new_elements)];
3080 Store[ELX][ELY] = new_element + RND(get_num_elements(new_element));
3081 Feld[ELX][ELY] = EL_GRAY_BALL_OPENING;
3083 /* !!! CHECK AGAIN: Laser on Polarizer !!! */
3094 element = EL_MIRROR_START + RND(16);
3100 element = (rnd == 0 ? EL_KETTLE : rnd == 1 ? EL_BOMB : EL_PRISM);
3107 element = (rnd == 0 ? EL_FUSE_ON :
3108 rnd >= 1 && rnd <= 4 ? EL_PACMAN_RIGHT + rnd - 1 :
3109 rnd >= 5 && rnd <= 20 ? EL_POLAR_START + rnd - 5 :
3110 rnd >= 21 && rnd <= 24 ? EL_POLAR_CROSS_START + rnd - 21 :
3111 EL_MIRROR_FIXED_START + rnd - 25);
3116 graphic = el2gfx(element);
3118 for (i = 0; i < 50; i++)
3124 BlitBitmap(pix[PIX_BACK], drawto,
3125 SX + (graphic % GFX_PER_LINE) * TILEX + x,
3126 SY + (graphic / GFX_PER_LINE) * TILEY + y, 6, 6,
3127 SX + ELX * TILEX + x,
3128 SY + ELY * TILEY + y);
3130 MarkTileDirty(ELX, ELY);
3133 DrawLaser(0, DL_LASER_ENABLED);
3138 Feld[ELX][ELY] = element;
3139 DrawField_MM(ELX, ELY);
3142 printf("NEW ELEMENT: (%d, %d)\n", ELX, ELY);
3145 /* above stuff: GRAY BALL -> PRISM !!! */
3147 LX = ELX * TILEX + 14;
3148 LY = ELY * TILEY + 14;
3149 if (laser.current_angle == (laser.current_angle >> 1) << 1)
3156 laser.num_edges -= 2;
3157 laser.num_damages--;
3161 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
3162 if (laser.damage[i].is_mirror)
3166 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3168 DrawLaser(0, DL_LASER_DISABLED);
3170 DrawLaser(0, DL_LASER_DISABLED);
3176 printf("TEST ELEMENT: %d\n", Feld[0][0]);
3183 if (IS_WALL_ICE(element) && CT > 1000)
3185 PlaySoundStereo(SND_SLURP, ST(ELX));
3188 Feld[ELX][ELY] = Feld[ELX][ELY] - EL_WALL_ICE + EL_WALL_CHANGING;
3189 Store[ELX][ELY] = EL_WALL_ICE;
3190 Store2[ELX][ELY] = laser.wall_mask;
3192 laser.dest_element = Feld[ELX][ELY];
3197 for (i = 0; i < 5; i++)
3203 Feld[ELX][ELY] &= (laser.wall_mask ^ 0xFF);
3207 DrawWallsAnimation_MM(ELX, ELY, Feld[ELX][ELY], phase, laser.wall_mask);
3212 if (Feld[ELX][ELY] == EL_WALL_ICE)
3213 Feld[ELX][ELY] = EL_EMPTY;
3217 LX = laser.edge[laser.num_edges].x - (SX + 2);
3218 LY = laser.edge[laser.num_edges].y - (SY + 2);
3221 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
3222 if (laser.damage[i].is_mirror)
3226 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3228 DrawLaser(0, DL_LASER_DISABLED);
3235 if (IS_WALL_AMOEBA(element) && CT > 1200)
3237 int k1, k2, k3, dx, dy, de, dm;
3238 int element2 = Feld[ELX][ELY];
3240 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element))
3243 for (i = laser.num_damages - 1; i >= 0; i--)
3244 if (laser.damage[i].is_mirror)
3247 r = laser.num_edges;
3248 d = laser.num_damages;
3255 DrawLaser(laser.damage[k1].edge - 1, DL_LASER_DISABLED);
3258 DrawLaser(0, DL_LASER_ENABLED);
3261 x = laser.damage[k1].x;
3262 y = laser.damage[k1].y;
3267 for (i = 0; i < 4; i++)
3269 if (laser.wall_mask & (1 << i))
3271 if (ReadPixel(drawto,
3272 SX + ELX * TILEX + 14 + (i % 2) * 2,
3273 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3275 if (ReadPixel(drawto,
3276 SX + ELX * TILEX + 31 * (i % 2),
3277 SY + ELY * TILEY + 14 + (i / 2) * 2) == pen_ray)
3284 for (i = 0; i < 4; i++)
3286 if (laser.wall_mask & (1 << i))
3288 if (ReadPixel(drawto,
3289 SX + ELX * TILEX + 31 * (i % 2),
3290 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3297 if (laser.num_beamers > 0 ||
3298 k1 < 1 || k2 < 4 || k3 < 4 ||
3299 ReadPixel(drawto, SX + ELX * TILEX + 14, SY + ELY * TILEY + 14)
3302 laser.num_edges = r;
3303 laser.num_damages = d;
3305 DrawLaser(0, DL_LASER_DISABLED);
3308 Feld[ELX][ELY] = element | laser.wall_mask;
3312 de = Feld[ELX][ELY];
3313 dm = laser.wall_mask;
3317 int x = ELX, y = ELY;
3318 int wall_mask = laser.wall_mask;
3321 DrawLaser(0, DL_LASER_ENABLED);
3323 PlaySoundStereo(SND_AMOEBE, ST(dx));
3325 Feld[x][y] = Feld[x][y] - EL_WALL_AMOEBA + EL_WALL_CHANGING;
3326 Store[x][y] = EL_WALL_AMOEBA;
3327 Store2[x][y] = wall_mask;
3333 DrawWallsAnimation_MM(dx, dy, de, 4, dm);
3335 DrawLaser(0, DL_LASER_ENABLED);
3337 PlaySoundStereo(SND_AMOEBE, ST(dx));
3339 for (i = 4; i >= 0; i--)
3341 DrawWallsAnimation_MM(dx, dy, de, i, dm);
3347 DrawLaser(0, DL_LASER_ENABLED);
3352 if ((element == EL_BLOCK_WOOD || element == EL_BLOCK_STONE) &&
3353 laser.stops_inside_element && CT > 1500)
3358 if (ABS(XS) > ABS(YS))
3365 for (i = 0; i < 4; i++)
3372 x = ELX + Step[k * 4].x;
3373 y = ELY + Step[k * 4].y;
3375 if (!IN_LEV_FIELD(x, y) || Feld[x][y] != EL_EMPTY)
3378 if (ObjHit(x, y, HIT_POS_CENTER | HIT_POS_EDGE | HIT_POS_BETWEEN))
3386 laser.overloaded = (element == EL_BLOCK_STONE);
3391 PlaySoundStereo(SND_BONG, ST(ELX));
3394 Feld[x][y] = element;
3396 DrawGraphic_MM(ELX, ELY, IMG_EMPTY);
3399 if (element == EL_BLOCK_STONE && Box[ELX][ELY])
3401 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
3402 DrawLaser(laser.num_edges - 1, DL_LASER_ENABLED);
3410 if (element == EL_FUEL_FULL && CT > 200)
3412 for (i = game_mm.energy_left; i <= MAX_LASER_ENERGY; i+=2)
3415 BlitBitmap(pix[PIX_DOOR], drawto,
3416 DOOR_GFX_PAGEX4 + XX_ENERGY,
3417 DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
3418 ENERGY_XSIZE, i, DX_ENERGY,
3419 DY_ENERGY + ENERGY_YSIZE - i);
3422 redraw_mask |= REDRAW_DOOR_1;
3428 game_mm.energy_left = MAX_LASER_ENERGY;
3429 Feld[ELX][ELY] = EL_FUEL_EMPTY;
3430 DrawField_MM(ELX, ELY);
3432 DrawLaser(0, DL_LASER_ENABLED);
3440 void GameActions_MM(struct MouseActionInfo action, boolean warp_mode)
3442 ClickElement(action.lx, action.ly, action.button);
3444 GameActions_MM_Ext(action, warp_mode);
3450 int mx, my, ox, oy, nx, ny;
3454 if (++p >= game_mm.num_pacman)
3457 game_mm.pacman[p].dir--;
3459 for (l = 1; l < 5; l++)
3461 game_mm.pacman[p].dir++;
3463 if (game_mm.pacman[p].dir > 4)
3464 game_mm.pacman[p].dir = 1;
3466 if (game_mm.pacman[p].dir % 2)
3469 my = game_mm.pacman[p].dir - 2;
3474 mx = 3 - game_mm.pacman[p].dir;
3477 ox = game_mm.pacman[p].x;
3478 oy = game_mm.pacman[p].y;
3481 element = Feld[nx][ny];
3483 if (nx < 0 || nx > 15 || ny < 0 || ny > 11)
3486 if (!IS_EATABLE4PACMAN(element))
3489 if (ObjHit(nx, ny, HIT_POS_CENTER))
3492 Feld[ox][oy] = EL_EMPTY;
3494 EL_PACMAN_RIGHT - 1 +
3495 (game_mm.pacman[p].dir - 1 +
3496 (game_mm.pacman[p].dir % 2) * 2);
3498 game_mm.pacman[p].x = nx;
3499 game_mm.pacman[p].y = ny;
3501 DrawGraphic_MM(ox, oy, IMG_EMPTY);
3503 if (element != EL_EMPTY)
3505 int graphic = el2gfx(Feld[nx][ny]);
3510 getGraphicSource(graphic, 0, &bitmap, &src_x, &src_y);
3513 ox = SX + ox * TILEX;
3514 oy = SY + oy * TILEY;
3516 for (i = 1; i < 33; i += 2)
3517 BlitBitmap(bitmap, window,
3518 src_x, src_y, TILEX, TILEY,
3519 ox + i * mx, oy + i * my);
3520 Ct = Ct + Counter() - CT;
3523 DrawField_MM(nx, ny);
3526 if (!laser.fuse_off)
3528 DrawLaser(0, DL_LASER_ENABLED);
3530 if (ObjHit(nx, ny, HIT_POS_BETWEEN))
3532 AddDamagedField(nx, ny);
3534 laser.damage[laser.num_damages - 1].edge = 0;
3538 if (element == EL_BOMB)
3539 DeletePacMan(nx, ny);
3541 if (IS_WALL_AMOEBA(element) &&
3542 (LX + 2 * XS) / TILEX == nx &&
3543 (LY + 2 * YS) / TILEY == ny)
3556 boolean raise_level = FALSE;
3559 if (local_player->MovPos)
3562 local_player->LevelSolved = FALSE;
3565 if (game_mm.energy_left)
3567 if (setup.sound_loops)
3568 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3569 SND_CTRL_PLAY_LOOP);
3571 while (game_mm.energy_left > 0)
3573 if (!setup.sound_loops)
3574 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3577 if (game_mm.energy_left > 0 && !(game_mm.energy_left % 10))
3578 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3583 game_mm.energy_left--;
3584 if (game_mm.energy_left >= 0)
3587 BlitBitmap(pix[PIX_DOOR], drawto,
3588 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
3589 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
3590 DX_ENERGY, DY_ENERGY);
3592 redraw_mask |= REDRAW_DOOR_1;
3599 if (setup.sound_loops)
3600 StopSound(SND_SIRR);
3602 else if (native_mm_level.time == 0) /* level without time limit */
3604 if (setup.sound_loops)
3605 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3606 SND_CTRL_PLAY_LOOP);
3608 while (TimePlayed < 999)
3610 if (!setup.sound_loops)
3611 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3612 if (TimePlayed < 999 && !(TimePlayed % 10))
3613 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3614 if (TimePlayed < 900 && !(TimePlayed % 10))
3620 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
3627 if (setup.sound_loops)
3628 StopSound(SND_SIRR);
3635 CloseDoor(DOOR_CLOSE_1);
3637 Request("Level solved !", REQ_CONFIRM);
3639 if (level_nr == leveldir_current->handicap_level)
3641 leveldir_current->handicap_level++;
3642 SaveLevelSetup_SeriesInfo();
3645 if (level_editor_test_game)
3646 game_mm.score = -1; /* no highscore when playing from editor */
3647 else if (level_nr < leveldir_current->last_level)
3648 raise_level = TRUE; /* advance to next level */
3650 if ((hi_pos = NewHiScore_MM()) >= 0)
3652 game_status = HALLOFFAME;
3654 // DrawHallOfFame(hi_pos);
3661 game_status = MAINMENU;
3677 // LoadScore(level_nr);
3679 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
3680 game_mm.score < highscore[MAX_SCORE_ENTRIES - 1].Score)
3683 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3685 if (game_mm.score > highscore[k].Score)
3687 /* player has made it to the hall of fame */
3689 if (k < MAX_SCORE_ENTRIES - 1)
3691 int m = MAX_SCORE_ENTRIES - 1;
3694 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3695 if (!strcmp(setup.player_name, highscore[l].Name))
3697 if (m == k) /* player's new highscore overwrites his old one */
3701 for (l = m; l>k; l--)
3703 strcpy(highscore[l].Name, highscore[l - 1].Name);
3704 highscore[l].Score = highscore[l - 1].Score;
3711 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3712 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3713 highscore[k].Score = game_mm.score;
3720 else if (!strncmp(setup.player_name, highscore[k].Name,
3721 MAX_PLAYER_NAME_LEN))
3722 break; /* player already there with a higher score */
3727 // if (position >= 0)
3728 // SaveScore(level_nr);
3733 static void InitMovingField_MM(int x, int y, int direction)
3735 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3736 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3738 MovDir[x][y] = direction;
3739 MovDir[newx][newy] = direction;
3741 if (Feld[newx][newy] == EL_EMPTY)
3742 Feld[newx][newy] = EL_BLOCKED;
3745 static void Moving2Blocked_MM(int x, int y, int *goes_to_x, int *goes_to_y)
3747 int direction = MovDir[x][y];
3748 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3749 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3755 static void Blocked2Moving_MM(int x, int y,
3756 int *comes_from_x, int *comes_from_y)
3758 int oldx = x, oldy = y;
3759 int direction = MovDir[x][y];
3761 if (direction == MV_LEFT)
3763 else if (direction == MV_RIGHT)
3765 else if (direction == MV_UP)
3767 else if (direction == MV_DOWN)
3770 *comes_from_x = oldx;
3771 *comes_from_y = oldy;
3774 static int MovingOrBlocked2Element_MM(int x, int y)
3776 int element = Feld[x][y];
3778 if (element == EL_BLOCKED)
3782 Blocked2Moving_MM(x, y, &oldx, &oldy);
3784 return Feld[oldx][oldy];
3791 static void RemoveField(int x, int y)
3793 Feld[x][y] = EL_EMPTY;
3800 static void RemoveMovingField_MM(int x, int y)
3802 int oldx = x, oldy = y, newx = x, newy = y;
3804 if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
3807 if (IS_MOVING(x, y))
3809 Moving2Blocked_MM(x, y, &newx, &newy);
3810 if (Feld[newx][newy] != EL_BLOCKED)
3813 else if (Feld[x][y] == EL_BLOCKED)
3815 Blocked2Moving_MM(x, y, &oldx, &oldy);
3816 if (!IS_MOVING(oldx, oldy))
3820 Feld[oldx][oldy] = EL_EMPTY;
3821 Feld[newx][newy] = EL_EMPTY;
3822 MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
3823 MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
3825 DrawLevelField_MM(oldx, oldy);
3826 DrawLevelField_MM(newx, newy);
3829 void PlaySoundLevel(int x, int y, int sound_nr)
3831 int sx = SCREENX(x), sy = SCREENY(y);
3833 int silence_distance = 8;
3835 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
3836 (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
3839 if (!IN_LEV_FIELD(x, y) ||
3840 sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
3841 sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
3844 volume = SOUND_MAX_VOLUME;
3847 stereo = (sx - SCR_FIELDX/2) * 12;
3849 stereo = SOUND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
3850 if (stereo > SOUND_MAX_RIGHT)
3851 stereo = SOUND_MAX_RIGHT;
3852 if (stereo < SOUND_MAX_LEFT)
3853 stereo = SOUND_MAX_LEFT;
3856 if (!IN_SCR_FIELD(sx, sy))
3858 int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
3859 int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
3861 volume -= volume * (dx > dy ? dx : dy) / silence_distance;
3864 PlaySoundExt(sound_nr, volume, stereo, SND_CTRL_PLAY_SOUND);
3867 static void RaiseScore_MM(int value)
3869 game_mm.score += value;
3872 DrawText(DX_SCORE, DY_SCORE, int2str(game_mm.score, 4),
3877 void RaiseScoreElement_MM(int element)
3882 RaiseScore_MM(native_mm_level.score[SC_PACMAN]);
3886 RaiseScore_MM(native_mm_level.score[SC_KEY]);