1 // ============================================================================
2 // Mirror Magic -- McDuffin's Revenge
3 // ----------------------------------------------------------------------------
4 // (c) 1994-2017 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
18 /* graphic position values for game controls */
19 #define ENERGY_XSIZE 32
20 #define ENERGY_YSIZE MAX_LASER_ENERGY
21 #define OVERLOAD_XSIZE ENERGY_XSIZE
22 #define OVERLOAD_YSIZE MAX_LASER_OVERLOAD
24 /* values for Explode_MM() */
25 #define EX_PHASE_START 0
30 /* special positions in the game control window (relative to control window) */
39 #define XX_OVERLOAD 60
40 #define YY_OVERLOAD YY_ENERGY
42 /* special positions in the game control window (relative to main window) */
43 #define DX_LEVEL (DX + XX_LEVEL)
44 #define DY_LEVEL (DY + YY_LEVEL)
45 #define DX_KETTLES (DX + XX_KETTLES)
46 #define DY_KETTLES (DY + YY_KETTLES)
47 #define DX_SCORE (DX + XX_SCORE)
48 #define DY_SCORE (DY + YY_SCORE)
49 #define DX_ENERGY (DX + XX_ENERGY)
50 #define DY_ENERGY (DY + YY_ENERGY)
51 #define DX_OVERLOAD (DX + XX_OVERLOAD)
52 #define DY_OVERLOAD (DY + YY_OVERLOAD)
54 #define IS_LOOP_SOUND(s) ((s) == SND_FUEL)
55 #define IS_MUSIC_SOUND(s) ((s) == SND_TYGER || (s) == SND_VOYAGER)
57 /* game button identifiers */
58 #define GAME_CTRL_ID_LEFT 0
59 #define GAME_CTRL_ID_MIDDLE 1
60 #define GAME_CTRL_ID_RIGHT 2
62 #define NUM_GAME_BUTTONS 3
64 /* values for DrawLaser() */
65 #define DL_LASER_DISABLED 0
66 #define DL_LASER_ENABLED 1
68 /* values for 'click_delay_value' in ClickElement() */
69 #define CLICK_DELAY_SHORT 125
70 #define CLICK_DELAY_LONG 250
71 #define AUTO_ROTATE_DELAY CLICK_DELAY_SHORT
73 /* forward declaration for internal use */
74 static int MovingOrBlocked2Element_MM(int, int);
75 static void Bang_MM(int, int);
76 static void RaiseScore_MM(int);
77 static void RemoveMovingField_MM(int, int);
78 static void InitMovingField_MM(int, int, int);
79 static void ContinueMoving_MM(int, int);
80 static void Moving2Blocked_MM(int, int, int *, int *);
83 static int get_element_angle(int element)
85 int element_phase = get_element_phase(element);
87 if (IS_MIRROR_FIXED(element) ||
88 IS_MCDUFFIN(element) ||
91 return 4 * element_phase;
96 static int get_opposite_angle(int angle)
98 int opposite_angle = angle + ANG_RAY_180;
100 /* make sure "opposite_angle" is in valid interval [0, 15] */
101 return (opposite_angle + 16) % 16;
104 static int get_mirrored_angle(int laser_angle, int mirror_angle)
106 int reflected_angle = 16 - laser_angle + mirror_angle;
108 /* make sure "reflected_angle" is in valid interval [0, 15] */
109 return (reflected_angle + 16) % 16;
112 static void InitMovDir_MM(int x, int y)
114 int element = Feld[x][y];
115 static int direction[3][4] =
117 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
118 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
119 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
124 case EL_PACMAN_RIGHT:
128 Feld[x][y] = EL_PACMAN;
129 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
137 static void InitField(int x, int y, boolean init_game)
139 int element = Feld[x][y];
144 Feld[x][y] = EL_EMPTY;
149 if (native_mm_level.auto_count_kettles)
150 game_mm.kettles_still_needed++;
153 case EL_LIGHTBULB_OFF:
154 game_mm.lights_still_needed++;
158 if (IS_MIRROR(element) ||
159 IS_BEAMER_OLD(element) ||
160 IS_BEAMER(element) ||
162 IS_POLAR_CROSS(element) ||
163 IS_DF_MIRROR(element) ||
164 IS_DF_MIRROR_AUTO(element) ||
165 IS_GRID_STEEL_AUTO(element) ||
166 IS_GRID_WOOD_AUTO(element) ||
167 IS_FIBRE_OPTIC(element))
169 if (IS_BEAMER_OLD(element))
171 Feld[x][y] = EL_BEAMER_BLUE_START + (element - EL_BEAMER_START);
172 element = Feld[x][y];
175 if (!IS_FIBRE_OPTIC(element))
177 static int steps_grid_auto = 0;
179 if (game_mm.num_cycle == 0) /* initialize cycle steps for grids */
180 steps_grid_auto = RND(16) * (RND(2) ? -1 : +1);
182 if (IS_GRID_STEEL_AUTO(element) ||
183 IS_GRID_WOOD_AUTO(element))
184 game_mm.cycle[game_mm.num_cycle].steps = steps_grid_auto;
186 game_mm.cycle[game_mm.num_cycle].steps = RND(16) * (RND(2) ? -1 : +1);
188 game_mm.cycle[game_mm.num_cycle].x = x;
189 game_mm.cycle[game_mm.num_cycle].y = y;
193 if (IS_BEAMER(element) || IS_FIBRE_OPTIC(element))
195 int beamer_nr = BEAMER_NR(element);
196 int nr = laser.beamer[beamer_nr][0].num;
198 laser.beamer[beamer_nr][nr].x = x;
199 laser.beamer[beamer_nr][nr].y = y;
200 laser.beamer[beamer_nr][nr].num = 1;
203 else if (IS_PACMAN(element))
207 else if (IS_MCDUFFIN(element) || IS_LASER(element))
209 laser.start_edge.x = x;
210 laser.start_edge.y = y;
211 laser.start_angle = get_element_angle(element);
218 static void InitCycleElements()
222 if (game_mm.num_cycle == 0) /* no elements to cycle */
225 for (i = 0; i < 16; i++)
227 for (j = 0; j < game_mm.num_cycle; j++)
229 int x = game_mm.cycle[j].x;
230 int y = game_mm.cycle[j].y;
231 int step = SIGN(game_mm.cycle[j].steps);
232 int last_element = Feld[x][y];
233 int next_element = get_rotated_element(last_element, step);
235 if (!game_mm.cycle[j].steps)
238 Feld[x][y] = next_element;
241 game_mm.cycle[j].steps -= step;
248 if (setup.quick_doors)
252 Delay(AUTO_ROTATE_DELAY);
256 static void InitLaser()
258 int start_element = Feld[laser.start_edge.x][laser.start_edge.y];
259 int step = (IS_LASER(start_element) ? 4 : 0);
261 LX = laser.start_edge.x * TILEX;
262 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
265 LX += (laser.start_angle == ANG_RAY_RIGHT ? 28 + step : 0 - step);
267 LY = laser.start_edge.y * TILEY;
268 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
269 LY += (laser.start_angle == ANG_RAY_DOWN ? 28 + step : 0 - step);
273 XS = 2 * Step[laser.start_angle].x;
274 YS = 2 * Step[laser.start_angle].y;
276 laser.current_angle = laser.start_angle;
278 laser.num_damages = 0;
280 laser.num_beamers = 0;
281 laser.beamer_edge[0] = 0;
283 AddLaserEdge(LX, LY); /* set laser starting edge */
285 pen_ray = GetPixelFromRGB(window,
286 native_mm_level.laser_red * 0xFF,
287 native_mm_level.laser_green * 0xFF,
288 native_mm_level.laser_blue * 0xFF);
291 void InitGameEngine_MM()
295 /* set global editor control values */
296 editor.draw_walls_masked = FALSE;
298 /* set global game control values */
299 game_mm.num_cycle = 0;
300 game_mm.num_pacman = 0;
303 game_mm.energy_left = native_mm_level.time;
304 game_mm.kettles_still_needed =
305 (native_mm_level.auto_count_kettles ? 0 : native_mm_level.kettles_needed);
306 game_mm.lights_still_needed = 0;
307 game_mm.num_keys = 0;
309 game_mm.level_solved = FALSE;
310 game_mm.game_over = FALSE;
311 game_mm.game_over_cause = 0;
313 /* set global laser control values (must be set before "InitLaser()") */
314 laser.start_edge.x = 0;
315 laser.start_edge.y = 0;
316 laser.start_angle = 0;
318 for (i = 0; i < MAX_NUM_BEAMERS; i++)
319 laser.beamer[i][0].num = laser.beamer[i][1].num = 0;
321 laser.overloaded = FALSE;
322 laser.overload_value = 0;
323 laser.fuse_off = FALSE;
324 laser.fuse_x = laser.fuse_y = -1;
326 laser.dest_element = EL_EMPTY;
331 for (x = 0; x < lev_fieldx; x++)
333 for (y = 0; y < lev_fieldy; y++)
335 Feld[x][y] = Ur[x][y];
336 Hit[x][y] = Box[x][y] = 0;
338 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
339 Store[x][y] = Store2[x][y] = 0;
343 InitField(x, y, TRUE);
348 CloseDoor(DOOR_CLOSE_1);
354 void InitGameEngine_MM_AfterFadingIn()
360 /* copy default game door content to main double buffer */
361 BlitBitmap(pix[PIX_DOOR], drawto,
362 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
366 DrawText(DX_LEVEL, DY_LEVEL,
367 int2str(level_nr, 2), FONT_TEXT_2);
368 DrawText(DX_KETTLES, DY_KETTLES,
369 int2str(game_mm.kettles_still_needed, 3), FONT_TEXT_2);
370 DrawText(DX_SCORE, DY_SCORE,
371 int2str(game_mm.score, 4), FONT_TEXT_2);
380 /* copy actual game door content to door double buffer for OpenDoor() */
381 BlitBitmap(drawto, pix[PIX_DB_DOOR],
382 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
386 OpenDoor(DOOR_OPEN_ALL);
389 if (setup.sound_loops)
390 PlaySoundExt(SND_FUEL, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT, SND_CTRL_PLAY_LOOP);
392 #if 0 // !!! TEMPORARILY DISABLED !!!
393 for (i = 0; i <= game_mm.energy_left; i += 2)
395 if (!setup.sound_loops)
396 PlaySoundStereo(SND_FUEL, SOUND_MAX_RIGHT);
399 BlitBitmap(pix[PIX_DOOR], drawto,
400 DOOR_GFX_PAGEX4 + XX_ENERGY,
401 DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
403 DX_ENERGY, DY_ENERGY + ENERGY_YSIZE - i);
406 redraw_mask |= REDRAW_DOOR_1;
412 if (setup.quick_doors)
419 if (setup.sound_loops)
424 if (setup.sound_music && num_bg_loops)
425 PlayMusic(level_nr % num_bg_loops);
431 void AddLaserEdge(int lx, int ly)
433 if (lx < -2 || ly < -2 || lx >= SXSIZE + 2 || ly >= SYSIZE + 2)
435 Error(ERR_WARN, "AddLaserEdge: out of bounds: %d, %d", lx, ly);
440 laser.edge[laser.num_edges].x = SX + 2 + lx;
441 laser.edge[laser.num_edges].y = SY + 2 + ly;
447 void AddDamagedField(int ex, int ey)
449 laser.damage[laser.num_damages].is_mirror = FALSE;
450 laser.damage[laser.num_damages].angle = laser.current_angle;
451 laser.damage[laser.num_damages].edge = laser.num_edges;
452 laser.damage[laser.num_damages].x = ex;
453 laser.damage[laser.num_damages].y = ey;
463 int last_x = laser.edge[laser.num_edges - 1].x - SX - 2;
464 int last_y = laser.edge[laser.num_edges - 1].y - SY - 2;
466 return ((x - last_x) * XS < 0 || (y - last_y) * YS < 0);
472 static int getMaskFromElement(int element)
474 if (IS_GRID(element))
475 return IMG_MM_MASK_GRID_1 + get_element_phase(element);
476 else if (IS_MCDUFFIN(element))
477 return IMG_MM_MASK_MCDUFFIN_RIGHT + get_element_phase(element);
478 else if (IS_RECTANGLE(element) || IS_DF_GRID(element))
479 return IMG_MM_MASK_RECTANGLE;
481 return IMG_MM_MASK_CIRCLE;
489 printf("ScanPixel: start scanning at (%d, %d) [%d, %d] [%d, %d]\n",
490 LX, LY, LX / TILEX, LY / TILEY, LX % TILEX, LY % TILEY);
493 /* follow laser beam until it hits something (at least the screen border) */
494 while (hit_mask == HIT_MASK_NO_HIT)
500 if (SX + LX < REAL_SX || SX + LX >= REAL_SX + FULL_SXSIZE ||
501 SY + LY < REAL_SY || SY + LY >= REAL_SY + FULL_SYSIZE)
503 printf("ScanPixel: touched screen border!\n");
509 for (i = 0; i < 4; i++)
511 int px = LX + (i % 2) * 2;
512 int py = LY + (i / 2) * 2;
515 int lx = (px + TILEX) / TILEX - 1; /* ...+TILEX...-1 to get correct */
516 int ly = (py + TILEY) / TILEY - 1; /* negative values! */
519 if (IN_LEV_FIELD(lx, ly))
521 int element = Feld[lx][ly];
523 if (element == EL_EMPTY || element == EL_EXPLODING_TRANSP)
527 else if (IS_WALL(element) || IS_WALL_CHANGING(element))
529 int pos = dy / MINI_TILEY * 2 + dx / MINI_TILEX;
531 pixel = ((element & (1 << pos)) ? 1 : 0);
535 int graphic_mask = getMaskFromElement(element);
540 getGraphicSource(graphic_mask, 0, &bitmap, &src_x, &src_y);
545 pixel = (ReadPixel(bitmap, mask_x, mask_y) ? 1 : 0);
550 pixel = (SX + px < REAL_SX || SX + px >= REAL_SX + FULL_SXSIZE ||
551 SY + py < REAL_SY || SY + py >= REAL_SY + FULL_SYSIZE);
554 if ((Sign[laser.current_angle] & (1 << i)) && pixel)
555 hit_mask |= (1 << i);
558 if (hit_mask == HIT_MASK_NO_HIT)
560 /* hit nothing -- go on with another step */
572 int end = 0, rf = laser.num_edges;
574 laser.overloaded = FALSE;
575 laser.stops_inside_element = FALSE;
577 DrawLaser(0, DL_LASER_ENABLED);
580 printf("Start scanning with LX == %d, LY == %d, XS == %d, YS == %d\n",
588 if (laser.num_edges > MAX_LASER_LEN || laser.num_damages > MAX_LASER_LEN)
591 laser.overloaded = TRUE;
596 hit_mask = ScanPixel();
599 printf("Hit something at LX == %d, LY == %d, XS == %d, YS == %d\n",
603 /* hit something -- check out what it was */
604 ELX = (LX + XS) / TILEX;
605 ELY = (LY + YS) / TILEY;
608 printf("hit_mask (1) == '%x' (%d, %d) (%d, %d)\n",
609 hit_mask, LX, LY, ELX, ELY);
612 if (!IN_LEV_FIELD(ELX, ELY) || !IN_PIX_FIELD(LX, LY))
615 laser.dest_element = element;
620 if (hit_mask == (HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT))
622 /* we have hit the top-right and bottom-left element --
623 choose the bottom-left one */
624 /* !!! THIS CAN BE DONE MORE INTELLIGENTLY, FOR EXAMPLE, IF ONE
625 ELEMENT WAS STEEL AND THE OTHER ONE WAS ICE => ALWAYS CHOOSE
626 THE ICE AND MELT IT AWAY INSTEAD OF OVERLOADING LASER !!! */
627 ELX = (LX - 2) / TILEX;
628 ELY = (LY + 2) / TILEY;
631 if (hit_mask == (HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT))
633 /* we have hit the top-left and bottom-right element --
634 choose the top-left one */
635 /* !!! SEE ABOVE !!! */
636 ELX = (LX - 2) / TILEX;
637 ELY = (LY - 2) / TILEY;
641 printf("hit_mask (2) == '%x' (%d, %d) (%d, %d)\n",
642 hit_mask, LX, LY, ELX, ELY);
645 element = Feld[ELX][ELY];
646 laser.dest_element = element;
649 printf("Hit element %d at (%d, %d) [%d, %d] [%d, %d] [%d]\n",
652 LX % TILEX, LY % TILEY,
657 if (!IN_LEV_FIELD(ELX, ELY))
658 printf("WARNING! (1) %d, %d (%d)\n", ELX, ELY, element);
661 if (element == EL_EMPTY)
663 if (!HitOnlyAnEdge(element, hit_mask))
666 else if (element == EL_FUSE_ON)
668 if (HitPolarizer(element, hit_mask))
671 else if (IS_GRID(element) || IS_DF_GRID(element))
673 if (HitPolarizer(element, hit_mask))
676 else if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD ||
677 element == EL_GATE_STONE || element == EL_GATE_WOOD)
679 if (HitBlock(element, hit_mask))
686 else if (IS_MCDUFFIN(element))
688 if (HitLaserSource(element, hit_mask))
691 else if ((element >= EL_EXIT_CLOSED && element <= EL_EXIT_OPEN) ||
692 IS_RECEIVER(element))
694 if (HitLaserDestination(element, hit_mask))
697 else if (IS_WALL(element))
699 if (IS_WALL_STEEL(element) || IS_DF_WALL_STEEL(element))
701 if (HitReflectingWalls(element, hit_mask))
706 if (HitAbsorbingWalls(element, hit_mask))
712 if (HitElement(element, hit_mask))
717 DrawLaser(rf - 1, DL_LASER_ENABLED);
718 rf = laser.num_edges;
722 if (laser.dest_element != Feld[ELX][ELY])
724 printf("ALARM: laser.dest_element == %d, Feld[ELX][ELY] == %d\n",
725 laser.dest_element, Feld[ELX][ELY]);
729 if (!end && !laser.stops_inside_element && !StepBehind())
732 printf("ScanLaser: Go one step back\n");
738 AddLaserEdge(LX, LY);
742 DrawLaser(rf - 1, DL_LASER_ENABLED);
747 if (!IN_LEV_FIELD(ELX, ELY))
748 printf("WARNING! (2) %d, %d\n", ELX, ELY);
752 void DrawLaserExt(int start_edge, int num_edges, int mode)
758 printf("DrawLaserExt: start_edge, num_edges, mode == %d, %d, %d\n",
759 start_edge, num_edges, mode);
764 Error(ERR_WARN, "DrawLaserExt: start_edge < 0");
771 Error(ERR_WARN, "DrawLaserExt: num_edges < 0");
777 if (mode == DL_LASER_DISABLED)
779 printf("DrawLaser: Delete laser from edge %d\n", start_edge);
783 /* now draw the laser to the backbuffer and (if enabled) to the screen */
784 DrawLines(drawto, &laser.edge[start_edge], num_edges,
785 (mode == DL_LASER_ENABLED ? pen_ray : pen_bg));
787 redraw_mask |= REDRAW_FIELD;
789 if (mode == DL_LASER_ENABLED)
792 /* after the laser was deleted, the "damaged" graphics must be restored */
793 if (laser.num_damages)
795 int damage_start = 0;
798 /* determine the starting edge, from which graphics need to be restored */
801 for (i = 0; i < laser.num_damages; i++)
803 if (laser.damage[i].edge == start_edge + 1)
812 /* restore graphics from this starting edge to the end of damage list */
813 for (i = damage_start; i < laser.num_damages; i++)
815 int lx = laser.damage[i].x;
816 int ly = laser.damage[i].y;
817 int element = Feld[lx][ly];
819 if (Hit[lx][ly] == laser.damage[i].edge)
820 if (!((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
823 if (Box[lx][ly] == laser.damage[i].edge)
826 if (IS_DRAWABLE(element))
827 DrawField_MM(lx, ly);
830 elx = laser.damage[damage_start].x;
831 ely = laser.damage[damage_start].y;
832 element = Feld[elx][ely];
835 if (IS_BEAMER(element))
839 for (i = 0; i < laser.num_beamers; i++)
840 printf("-> %d\n", laser.beamer_edge[i]);
841 printf("DrawLaserExt: IS_BEAMER: [%d]: Hit[%d][%d] == %d [%d]\n",
842 mode, elx, ely, Hit[elx][ely], start_edge);
843 printf("DrawLaserExt: IS_BEAMER: %d / %d\n",
844 get_element_angle(element), laser.damage[damage_start].angle);
848 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
849 laser.num_beamers > 0 &&
850 start_edge == laser.beamer_edge[laser.num_beamers - 1])
852 /* element is outgoing beamer */
853 laser.num_damages = damage_start + 1;
855 if (IS_BEAMER(element))
856 laser.current_angle = get_element_angle(element);
860 /* element is incoming beamer or other element */
861 laser.num_damages = damage_start;
862 laser.current_angle = laser.damage[laser.num_damages].angle;
867 /* no damages but McDuffin himself (who needs to be redrawn anyway) */
869 elx = laser.start_edge.x;
870 ely = laser.start_edge.y;
871 element = Feld[elx][ely];
874 laser.num_edges = start_edge + 1;
876 laser.current_angle = laser.start_angle;
878 LX = laser.edge[start_edge].x - (SX + 2);
879 LY = laser.edge[start_edge].y - (SY + 2);
880 XS = 2 * Step[laser.current_angle].x;
881 YS = 2 * Step[laser.current_angle].y;
884 printf("DrawLaser: Set (LX, LY) to (%d, %d) [%d]\n",
890 if (IS_BEAMER(element) ||
891 IS_FIBRE_OPTIC(element) ||
892 IS_PACMAN(element) ||
894 IS_POLAR_CROSS(element) ||
895 element == EL_FUSE_ON)
900 printf("element == %d\n", element);
903 if (IS_22_5_ANGLE(laser.current_angle)) /* neither 90° nor 45° angle */
904 step_size = ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) ? 4 : 3);
908 if (IS_POLAR(element) || IS_POLAR_CROSS(element) ||
909 ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
910 (laser.num_beamers == 0 ||
911 start_edge != laser.beamer_edge[laser.num_beamers - 1])))
913 /* element is incoming beamer or other element */
914 step_size = -step_size;
919 if (IS_BEAMER(element))
921 printf("start_edge == %d, laser.beamer_edge == %d\n",
922 start_edge, laser.beamer_edge);
926 LX += step_size * XS;
927 LY += step_size * YS;
929 else if (element != EL_EMPTY)
938 printf("DrawLaser: Finally: (LX, LY) to (%d, %d) [%d]\n",
943 void DrawLaser(int start_edge, int mode)
945 if (laser.num_edges - start_edge < 0)
947 Error(ERR_WARN, "DrawLaser: laser.num_edges - start_edge < 0");
952 /* check if laser is interrupted by beamer element */
953 if (laser.num_beamers > 0 &&
954 start_edge < laser.beamer_edge[laser.num_beamers - 1])
956 if (mode == DL_LASER_ENABLED)
959 int tmp_start_edge = start_edge;
961 /* draw laser segments forward from the start to the last beamer */
962 for (i = 0; i < laser.num_beamers; i++)
964 int tmp_num_edges = laser.beamer_edge[i] - tmp_start_edge;
966 if (tmp_num_edges <= 0)
970 printf("DrawLaser: DL_LASER_ENABLED: i==%d: %d, %d\n",
971 i, laser.beamer_edge[i], tmp_start_edge);
974 DrawLaserExt(tmp_start_edge, tmp_num_edges, DL_LASER_ENABLED);
976 tmp_start_edge = laser.beamer_edge[i];
979 /* draw last segment from last beamer to the end */
980 DrawLaserExt(tmp_start_edge, laser.num_edges - tmp_start_edge,
986 int last_num_edges = laser.num_edges;
987 int num_beamers = laser.num_beamers;
989 /* delete laser segments backward from the end to the first beamer */
990 for (i = num_beamers-1; i >= 0; i--)
992 int tmp_num_edges = last_num_edges - laser.beamer_edge[i];
994 if (laser.beamer_edge[i] - start_edge <= 0)
997 DrawLaserExt(laser.beamer_edge[i], tmp_num_edges, DL_LASER_DISABLED);
999 last_num_edges = laser.beamer_edge[i];
1000 laser.num_beamers--;
1004 if (last_num_edges - start_edge <= 0)
1005 printf("DrawLaser: DL_LASER_DISABLED: %d, %d\n",
1006 last_num_edges, start_edge);
1009 /* delete first segment from start to the first beamer */
1010 DrawLaserExt(start_edge, last_num_edges - start_edge, DL_LASER_DISABLED);
1015 DrawLaserExt(start_edge, laser.num_edges - start_edge, mode);
1019 boolean HitElement(int element, int hit_mask)
1021 if (HitOnlyAnEdge(element, hit_mask))
1024 if (IS_MOVING(ELX, ELY) || IS_BLOCKED(ELX, ELY))
1025 element = MovingOrBlocked2Element_MM(ELX, ELY);
1028 printf("HitElement (1): element == %d\n", element);
1032 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1033 printf("HitElement (%d): EXACT MATCH @ (%d, %d)\n", element, ELX, ELY);
1035 printf("HitElement (%d): FUZZY MATCH @ (%d, %d)\n", element, ELX, ELY);
1038 AddDamagedField(ELX, ELY);
1040 /* this is more precise: check if laser would go through the center */
1041 if ((ELX * TILEX + 14 - LX) * YS != (ELY * TILEY + 14 - LY) * XS)
1043 /* skip the whole element before continuing the scan */
1049 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1051 if (LX/TILEX > ELX || LY/TILEY > ELY)
1053 /* skipping scan positions to the right and down skips one scan
1054 position too much, because this is only the top left scan position
1055 of totally four scan positions (plus one to the right, one to the
1056 bottom and one to the bottom right) */
1066 printf("HitElement (2): element == %d\n", element);
1069 if (LX + 5 * XS < 0 ||
1079 printf("HitElement (3): element == %d\n", element);
1082 if (IS_POLAR(element) &&
1083 ((element - EL_POLAR_START) % 2 ||
1084 (element - EL_POLAR_START) / 2 != laser.current_angle % 8))
1086 PlaySoundStereo(SND_KINK, ST(ELX));
1088 laser.num_damages--;
1093 if (IS_POLAR_CROSS(element) &&
1094 (element - EL_POLAR_CROSS_START) != laser.current_angle % 4)
1096 PlaySoundStereo(SND_KINK, ST(ELX));
1098 laser.num_damages--;
1103 if (!IS_BEAMER(element) &&
1104 !IS_FIBRE_OPTIC(element) &&
1105 !IS_GRID_WOOD(element) &&
1106 element != EL_FUEL_EMPTY)
1109 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1110 printf("EXACT MATCH @ (%d, %d)\n", ELX, ELY);
1112 printf("FUZZY MATCH @ (%d, %d)\n", ELX, ELY);
1115 LX = ELX * TILEX + 14;
1116 LY = ELY * TILEY + 14;
1118 AddLaserEdge(LX, LY);
1121 if (IS_MIRROR(element) ||
1122 IS_MIRROR_FIXED(element) ||
1123 IS_POLAR(element) ||
1124 IS_POLAR_CROSS(element) ||
1125 IS_DF_MIRROR(element) ||
1126 IS_DF_MIRROR_AUTO(element) ||
1127 element == EL_PRISM ||
1128 element == EL_REFRACTOR)
1130 int current_angle = laser.current_angle;
1133 laser.num_damages--;
1135 AddDamagedField(ELX, ELY);
1137 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1140 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1142 if (IS_MIRROR(element) ||
1143 IS_MIRROR_FIXED(element) ||
1144 IS_DF_MIRROR(element) ||
1145 IS_DF_MIRROR_AUTO(element))
1146 laser.current_angle = get_mirrored_angle(laser.current_angle,
1147 get_element_angle(element));
1149 if (element == EL_PRISM || element == EL_REFRACTOR)
1150 laser.current_angle = RND(16);
1152 XS = 2 * Step[laser.current_angle].x;
1153 YS = 2 * Step[laser.current_angle].y;
1155 if (!IS_22_5_ANGLE(laser.current_angle)) /* 90° or 45° angle */
1160 LX += step_size * XS;
1161 LY += step_size * YS;
1164 /* draw sparkles on mirror */
1165 if ((IS_MIRROR(element) || IS_MIRROR_FIXED(element)) &&
1166 current_angle != laser.current_angle)
1168 MoveSprite(vp, &Pfeil[2], 4 + 16 * ELX, 5 + 16 * ELY + 1);
1172 if ((!IS_POLAR(element) && !IS_POLAR_CROSS(element)) &&
1173 current_angle != laser.current_angle)
1174 PlaySoundStereo(SND_LASER, ST(ELX));
1177 (get_opposite_angle(laser.current_angle) ==
1178 laser.damage[laser.num_damages - 1].angle ? TRUE : FALSE);
1180 return (laser.overloaded ? TRUE : FALSE);
1183 if (element == EL_FUEL_FULL)
1185 laser.stops_inside_element = TRUE;
1190 if (element == EL_BOMB || element == EL_MINE)
1192 PlaySoundStereo(SND_KINK, ST(ELX));
1194 if (element == EL_MINE)
1195 laser.overloaded = TRUE;
1198 if (element == EL_KETTLE ||
1199 element == EL_CELL ||
1200 element == EL_KEY ||
1201 element == EL_LIGHTBALL ||
1202 element == EL_PACMAN ||
1205 if (!IS_PACMAN(element))
1208 if (element == EL_PACMAN)
1211 if (element == EL_KETTLE || element == EL_CELL)
1213 if (game_mm.kettles_still_needed > 0)
1214 game_mm.kettles_still_needed--;
1218 if (game_mm.kettles_still_needed == 0)
1221 static int xy[4][2] =
1229 PlaySoundStereo(SND_KLING, ST(ELX));
1231 for (y = 0; y < lev_fieldy; y++)
1233 for (x = 0; x < lev_fieldx; x++)
1235 /* initiate opening animation of exit door */
1236 if (Feld[x][y] == EL_EXIT_CLOSED)
1237 Feld[x][y] = EL_EXIT_OPENING;
1239 /* remove field that blocks receiver */
1240 if (IS_RECEIVER(Feld[x][y]))
1242 int phase = Feld[x][y] - EL_RECEIVER_START;
1243 int blocking_x, blocking_y;
1245 blocking_x = x + xy[phase][0];
1246 blocking_y = y + xy[phase][1];
1248 if (IN_LEV_FIELD(blocking_x, blocking_y))
1250 Feld[blocking_x][blocking_y] = EL_EMPTY;
1252 DrawField_MM(blocking_x, blocking_y);
1258 DrawLaser(0, DL_LASER_ENABLED);
1261 else if (element == EL_KEY)
1265 else if (element == EL_LIGHTBALL)
1269 else if (IS_PACMAN(element))
1271 DeletePacMan(ELX, ELY);
1278 if (element == EL_LIGHTBULB_OFF || element == EL_LIGHTBULB_ON)
1280 PlaySoundStereo(SND_KINK, ST(ELX));
1282 DrawLaser(0, DL_LASER_ENABLED);
1284 if (Feld[ELX][ELY] == EL_LIGHTBULB_OFF)
1286 Feld[ELX][ELY] = EL_LIGHTBULB_ON;
1287 game_mm.lights_still_needed--;
1291 Feld[ELX][ELY] = EL_LIGHTBULB_OFF;
1292 game_mm.lights_still_needed++;
1295 DrawField_MM(ELX, ELY);
1296 DrawLaser(0, DL_LASER_ENABLED);
1301 laser.stops_inside_element = TRUE;
1307 printf("HitElement (4): element == %d\n", element);
1310 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1311 laser.num_beamers < MAX_NUM_BEAMERS &&
1312 laser.beamer[BEAMER_NR(element)][1].num)
1314 int beamer_angle = get_element_angle(element);
1315 int beamer_nr = BEAMER_NR(element);
1319 printf("HitElement (BEAMER): element == %d\n", element);
1322 laser.num_damages--;
1324 if (IS_FIBRE_OPTIC(element) ||
1325 laser.current_angle == get_opposite_angle(beamer_angle))
1329 LX = ELX * TILEX + 14;
1330 LY = ELY * TILEY + 14;
1332 AddLaserEdge(LX, LY);
1333 AddDamagedField(ELX, ELY);
1335 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1338 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1340 pos = (ELX == laser.beamer[beamer_nr][0].x &&
1341 ELY == laser.beamer[beamer_nr][0].y ? 1 : 0);
1342 ELX = laser.beamer[beamer_nr][pos].x;
1343 ELY = laser.beamer[beamer_nr][pos].y;
1344 LX = ELX * TILEX + 14;
1345 LY = ELY * TILEY + 14;
1347 if (IS_BEAMER(element))
1349 laser.current_angle = get_element_angle(Feld[ELX][ELY]);
1350 XS = 2 * Step[laser.current_angle].x;
1351 YS = 2 * Step[laser.current_angle].y;
1354 laser.beamer_edge[laser.num_beamers] = laser.num_edges;
1356 AddLaserEdge(LX, LY);
1357 AddDamagedField(ELX, ELY);
1359 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1362 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1364 if (laser.current_angle == (laser.current_angle >> 1) << 1)
1369 LX += step_size * XS;
1370 LY += step_size * YS;
1372 laser.num_beamers++;
1381 boolean HitOnlyAnEdge(int element, int hit_mask)
1383 /* check if the laser hit only the edge of an element and, if so, go on */
1386 printf("LX, LY, hit_mask == %d, %d, %d\n", LX, LY, hit_mask);
1389 if ((hit_mask == HIT_MASK_TOPLEFT ||
1390 hit_mask == HIT_MASK_TOPRIGHT ||
1391 hit_mask == HIT_MASK_BOTTOMLEFT ||
1392 hit_mask == HIT_MASK_BOTTOMRIGHT) &&
1393 laser.current_angle % 4) /* angle is not 90° */
1397 if (hit_mask == HIT_MASK_TOPLEFT)
1402 else if (hit_mask == HIT_MASK_TOPRIGHT)
1407 else if (hit_mask == HIT_MASK_BOTTOMLEFT)
1412 else /* (hit_mask == HIT_MASK_BOTTOMRIGHT) */
1418 AddDamagedField((LX + 2 * dx) / TILEX, (LY + 2 * dy) / TILEY);
1424 printf("[HitOnlyAnEdge() == TRUE]\n");
1431 printf("[HitOnlyAnEdge() == FALSE]\n");
1437 boolean HitPolarizer(int element, int hit_mask)
1439 if (HitOnlyAnEdge(element, hit_mask))
1442 if (IS_DF_GRID(element))
1444 int grid_angle = get_element_angle(element);
1447 printf("HitPolarizer: angle: grid == %d, laser == %d\n",
1448 grid_angle, laser.current_angle);
1451 AddLaserEdge(LX, LY);
1452 AddDamagedField(ELX, ELY);
1455 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1457 if (laser.current_angle == grid_angle ||
1458 laser.current_angle == get_opposite_angle(grid_angle))
1460 /* skip the whole element before continuing the scan */
1466 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1468 if (LX/TILEX > ELX || LY/TILEY > ELY)
1470 /* skipping scan positions to the right and down skips one scan
1471 position too much, because this is only the top left scan position
1472 of totally four scan positions (plus one to the right, one to the
1473 bottom and one to the bottom right) */
1479 AddLaserEdge(LX, LY);
1485 printf("HitPolarizer: LX, LY == %d, %d [%d, %d] [%d, %d]\n",
1487 LX / TILEX, LY / TILEY,
1488 LX % TILEX, LY % TILEY);
1493 else if (IS_GRID_STEEL_FIXED(element) || IS_GRID_STEEL_AUTO(element))
1495 return HitReflectingWalls(element, hit_mask);
1499 return HitAbsorbingWalls(element, hit_mask);
1502 else if (IS_GRID_STEEL(element))
1504 return HitReflectingWalls(element, hit_mask);
1506 else /* IS_GRID_WOOD */
1508 return HitAbsorbingWalls(element, hit_mask);
1514 boolean HitBlock(int element, int hit_mask)
1516 boolean check = FALSE;
1518 if ((element == EL_GATE_STONE || element == EL_GATE_WOOD) &&
1519 game_mm.num_keys == 0)
1522 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1525 int ex = ELX * TILEX + 14;
1526 int ey = ELY * TILEY + 14;
1530 for (i = 1; i < 32; i++)
1535 if ((x == ex || x == ex + 1) && (y == ey || y == ey + 1))
1540 if (check && (element == EL_BLOCK_WOOD || element == EL_GATE_WOOD))
1541 return HitAbsorbingWalls(element, hit_mask);
1545 AddLaserEdge(LX - XS, LY - YS);
1546 AddDamagedField(ELX, ELY);
1549 Box[ELX][ELY] = laser.num_edges;
1551 return HitReflectingWalls(element, hit_mask);
1554 if (element == EL_GATE_STONE || element == EL_GATE_WOOD)
1556 int xs = XS / 2, ys = YS / 2;
1557 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1558 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1560 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1561 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1563 laser.overloaded = (element == EL_GATE_STONE);
1568 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1569 (hit_mask == HIT_MASK_TOP ||
1570 hit_mask == HIT_MASK_LEFT ||
1571 hit_mask == HIT_MASK_RIGHT ||
1572 hit_mask == HIT_MASK_BOTTOM))
1573 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1574 hit_mask == HIT_MASK_BOTTOM),
1575 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1576 hit_mask == HIT_MASK_RIGHT));
1577 AddLaserEdge(LX, LY);
1583 if (element == EL_GATE_STONE && Box[ELX][ELY])
1585 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
1597 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1599 int xs = XS / 2, ys = YS / 2;
1600 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1601 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1603 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1604 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1606 laser.overloaded = (element == EL_BLOCK_STONE);
1611 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1612 (hit_mask == HIT_MASK_TOP ||
1613 hit_mask == HIT_MASK_LEFT ||
1614 hit_mask == HIT_MASK_RIGHT ||
1615 hit_mask == HIT_MASK_BOTTOM))
1616 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1617 hit_mask == HIT_MASK_BOTTOM),
1618 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1619 hit_mask == HIT_MASK_RIGHT));
1620 AddDamagedField(ELX, ELY);
1622 LX = ELX * TILEX + 14;
1623 LY = ELY * TILEY + 14;
1625 AddLaserEdge(LX, LY);
1627 laser.stops_inside_element = TRUE;
1635 boolean HitLaserSource(int element, int hit_mask)
1637 if (HitOnlyAnEdge(element, hit_mask))
1640 PlaySoundStereo(SND_AUTSCH, ST(ELX));
1641 laser.overloaded = TRUE;
1646 boolean HitLaserDestination(int element, int hit_mask)
1648 if (HitOnlyAnEdge(element, hit_mask))
1651 if (element != EL_EXIT_OPEN &&
1652 !(IS_RECEIVER(element) &&
1653 game_mm.kettles_still_needed == 0 &&
1654 laser.current_angle == get_opposite_angle(get_element_angle(element))))
1656 PlaySoundStereo(SND_HOLZ, ST(ELX));
1660 if (IS_RECEIVER(element) ||
1661 (IS_22_5_ANGLE(laser.current_angle) &&
1662 (ELX != (LX + 6 * XS) / TILEX ||
1663 ELY != (LY + 6 * YS) / TILEY ||
1672 LX = ELX * TILEX + 14;
1673 LY = ELY * TILEY + 14;
1675 laser.stops_inside_element = TRUE;
1678 AddLaserEdge(LX, LY);
1679 AddDamagedField(ELX, ELY);
1681 if (game_mm.lights_still_needed == 0)
1682 game_mm.level_solved = TRUE;
1687 boolean HitReflectingWalls(int element, int hit_mask)
1689 /* check if laser hits side of a wall with an angle that is not 90° */
1690 if (!IS_90_ANGLE(laser.current_angle) && (hit_mask == HIT_MASK_TOP ||
1691 hit_mask == HIT_MASK_LEFT ||
1692 hit_mask == HIT_MASK_RIGHT ||
1693 hit_mask == HIT_MASK_BOTTOM))
1695 PlaySoundStereo(SND_HUI, ST(ELX));
1700 if (!IS_DF_GRID(element))
1701 AddLaserEdge(LX, LY);
1703 /* check if laser hits wall with an angle of 45° */
1704 if (!IS_22_5_ANGLE(laser.current_angle))
1706 if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1709 laser.current_angle = get_mirrored_angle(laser.current_angle,
1712 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1715 laser.current_angle = get_mirrored_angle(laser.current_angle,
1719 AddLaserEdge(LX, LY);
1721 XS = 2 * Step[laser.current_angle].x;
1722 YS = 2 * Step[laser.current_angle].y;
1726 else if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1728 laser.current_angle = get_mirrored_angle(laser.current_angle,
1733 if (!IS_DF_GRID(element))
1734 AddLaserEdge(LX, LY);
1739 if (!IS_DF_GRID(element))
1740 AddLaserEdge(LX, LY + YS / 2);
1743 if (!IS_DF_GRID(element))
1744 AddLaserEdge(LX, LY);
1747 YS = 2 * Step[laser.current_angle].y;
1751 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1753 laser.current_angle = get_mirrored_angle(laser.current_angle,
1758 if (!IS_DF_GRID(element))
1759 AddLaserEdge(LX, LY);
1764 if (!IS_DF_GRID(element))
1765 AddLaserEdge(LX + XS / 2, LY);
1768 if (!IS_DF_GRID(element))
1769 AddLaserEdge(LX, LY);
1772 XS = 2 * Step[laser.current_angle].x;
1778 /* reflection at the edge of reflecting DF style wall */
1779 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1781 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1782 hit_mask == HIT_MASK_TOPRIGHT) ||
1783 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1784 hit_mask == HIT_MASK_TOPLEFT) ||
1785 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1786 hit_mask == HIT_MASK_BOTTOMLEFT) ||
1787 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1788 hit_mask == HIT_MASK_BOTTOMRIGHT))
1791 (hit_mask == HIT_MASK_TOPRIGHT || hit_mask == HIT_MASK_BOTTOMLEFT ?
1792 ANG_MIRROR_135 : ANG_MIRROR_45);
1794 PlaySoundStereo(SND_HUI, ST(ELX));
1796 AddDamagedField(ELX, ELY);
1797 AddLaserEdge(LX, LY);
1799 laser.current_angle = get_mirrored_angle(laser.current_angle,
1807 AddLaserEdge(LX, LY);
1813 /* reflection inside an edge of reflecting DF style wall */
1814 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1816 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1817 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT)) ||
1818 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1819 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMRIGHT)) ||
1820 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1821 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT)) ||
1822 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1823 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPLEFT)))
1826 (hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT) ||
1827 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT) ?
1828 ANG_MIRROR_135 : ANG_MIRROR_45);
1830 PlaySoundStereo(SND_HUI, ST(ELX));
1833 AddDamagedField(ELX, ELY);
1836 AddLaserEdge(LX - XS, LY - YS);
1837 AddLaserEdge(LX - XS + (ABS(XS) == 4 ? XS/2 : 0),
1838 LY - YS + (ABS(YS) == 4 ? YS/2 : 0));
1840 laser.current_angle = get_mirrored_angle(laser.current_angle,
1848 AddLaserEdge(LX, LY);
1854 /* check if laser hits DF style wall with an angle of 90° */
1855 if (IS_DF_WALL(element) && IS_90_ANGLE(laser.current_angle))
1857 if ((IS_HORIZ_ANGLE(laser.current_angle) &&
1858 (!(hit_mask & HIT_MASK_TOP) || !(hit_mask & HIT_MASK_BOTTOM))) ||
1859 (IS_VERT_ANGLE(laser.current_angle) &&
1860 (!(hit_mask & HIT_MASK_LEFT) || !(hit_mask & HIT_MASK_RIGHT))))
1862 static int last_LX = 0, last_LY = 0, last_hit_mask = 0;
1864 /* laser at last step touched nothing or the same side of the wall */
1865 if (LX != last_LX || LY != last_LY || hit_mask == last_hit_mask)
1867 AddDamagedField(ELX, ELY);
1874 last_hit_mask = hit_mask;
1881 if (!HitOnlyAnEdge(element, hit_mask))
1883 laser.overloaded = TRUE;
1891 boolean HitAbsorbingWalls(int element, int hit_mask)
1893 if (HitOnlyAnEdge(element, hit_mask))
1897 (hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT))
1899 AddLaserEdge(LX - XS, LY - YS);
1906 (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM))
1908 AddLaserEdge(LX - XS, LY - YS);
1914 if (IS_WALL_WOOD(element) ||
1915 IS_DF_WALL_WOOD(element) ||
1916 IS_GRID_WOOD(element) ||
1917 IS_GRID_WOOD_FIXED(element) ||
1918 IS_GRID_WOOD_AUTO(element) ||
1919 element == EL_FUSE_ON ||
1920 element == EL_BLOCK_WOOD ||
1921 element == EL_GATE_WOOD)
1923 PlaySoundStereo(SND_HOLZ, ST(ELX));
1928 if (IS_WALL_ICE(element))
1932 mask = (LX + XS) / MINI_TILEX - ELX * 2 + 1; /* Quadrant (horizontal) */
1933 mask <<= (((LY + YS) / MINI_TILEY - ELY * 2) > 0) * 2; /* || (vertical) */
1935 /* check if laser hits wall with an angle of 90° */
1936 if (IS_90_ANGLE(laser.current_angle))
1937 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1939 if (mask == 1 || mask == 2 || mask == 4 || mask == 8)
1943 for (i = 0; i < 4; i++)
1945 if (mask == (1 << i) && (XS > 0) == (i % 2) && (YS > 0) == (i / 2))
1946 mask = 15 - (8 >> i);
1947 else if (ABS(XS) == 4 &&
1949 (XS > 0) == (i % 2) &&
1950 (YS < 0) == (i / 2))
1951 mask = 3 + (i / 2) * 9;
1952 else if (ABS(YS) == 4 &&
1954 (XS < 0) == (i % 2) &&
1955 (YS > 0) == (i / 2))
1956 mask = 5 + (i % 2) * 5;
1960 laser.wall_mask = mask;
1962 else if (IS_WALL_AMOEBA(element))
1964 int elx = (LX - 2 * XS) / TILEX;
1965 int ely = (LY - 2 * YS) / TILEY;
1966 int element2 = Feld[elx][ely];
1969 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element2))
1971 laser.dest_element = EL_EMPTY;
1979 mask = (LX - 2 * XS) / 16 - ELX * 2 + 1;
1980 mask <<= ((LY - 2 * YS) / 16 - ELY * 2) * 2;
1982 if (IS_90_ANGLE(laser.current_angle))
1983 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1985 laser.dest_element = element2 | EL_WALL_AMOEBA;
1987 laser.wall_mask = mask;
1993 void OpenExit(int x, int y)
1997 if (!MovDelay[x][y]) /* next animation frame */
1998 MovDelay[x][y] = 4 * delay;
2000 if (MovDelay[x][y]) /* wait some time before next frame */
2005 phase = MovDelay[x][y] / delay;
2007 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2008 DrawGraphicAnimation_MM(x, y, IMG_MM_EXIT_OPENING, 3 - phase);
2010 if (!MovDelay[x][y])
2012 Feld[x][y] = EL_EXIT_OPEN;
2018 void OpenSurpriseBall(int x, int y)
2022 if (!MovDelay[x][y]) /* next animation frame */
2023 MovDelay[x][y] = 50 * delay;
2025 if (MovDelay[x][y]) /* wait some time before next frame */
2029 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2032 int graphic = el2gfx(Store[x][y]);
2034 int dx = RND(26), dy = RND(26);
2036 getGraphicSource(graphic, 0, &bitmap, &gx, &gy);
2038 BlitBitmap(bitmap, drawto, gx + dx, gy + dy, 6, 6,
2039 SX + x * TILEX + dx, SY + y * TILEY + dy);
2041 MarkTileDirty(x, y);
2044 if (!MovDelay[x][y])
2046 Feld[x][y] = Store[x][y];
2055 void MeltIce(int x, int y)
2060 if (!MovDelay[x][y]) /* next animation frame */
2061 MovDelay[x][y] = frames * delay;
2063 if (MovDelay[x][y]) /* wait some time before next frame */
2066 int wall_mask = Store2[x][y];
2067 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_ICE;
2070 phase = frames - MovDelay[x][y] / delay - 1;
2072 if (!MovDelay[x][y])
2076 Feld[x][y] = real_element & (wall_mask ^ 0xFF);
2077 Store[x][y] = Store2[x][y] = 0;
2079 DrawWalls_MM(x, y, Feld[x][y]);
2081 if (Feld[x][y] == EL_WALL_ICE)
2082 Feld[x][y] = EL_EMPTY;
2084 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
2085 if (laser.damage[i].is_mirror)
2089 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
2091 DrawLaser(0, DL_LASER_DISABLED);
2095 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2097 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2099 laser.redraw = TRUE;
2104 void GrowAmoeba(int x, int y)
2109 if (!MovDelay[x][y]) /* next animation frame */
2110 MovDelay[x][y] = frames * delay;
2112 if (MovDelay[x][y]) /* wait some time before next frame */
2115 int wall_mask = Store2[x][y];
2116 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_AMOEBA;
2119 phase = MovDelay[x][y] / delay;
2121 if (!MovDelay[x][y])
2123 Feld[x][y] = real_element;
2124 Store[x][y] = Store2[x][y] = 0;
2126 DrawWalls_MM(x, y, Feld[x][y]);
2127 DrawLaser(0, DL_LASER_ENABLED);
2129 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2131 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2136 static void Explode_MM(int x, int y, int phase, int mode)
2138 int num_phase = 9, delay = 2;
2139 int last_phase = num_phase * delay;
2140 int half_phase = (num_phase / 2) * delay;
2142 laser.redraw = TRUE;
2144 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2146 int center_element = Feld[x][y];
2148 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2150 /* put moving element to center field (and let it explode there) */
2151 center_element = MovingOrBlocked2Element_MM(x, y);
2152 RemoveMovingField_MM(x, y);
2154 Feld[x][y] = center_element;
2157 if (center_element == EL_BOMB || IS_MCDUFFIN(center_element))
2158 Store[x][y] = center_element;
2160 Store[x][y] = EL_EMPTY;
2162 Store2[x][y] = mode;
2163 Feld[x][y] = EL_EXPLODING_OPAQUE;
2164 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2170 Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
2172 if (phase == half_phase)
2174 Feld[x][y] = EL_EXPLODING_TRANSP;
2176 if (x == ELX && y == ELY)
2180 if (phase == last_phase)
2182 if (Store[x][y] == EL_BOMB)
2184 laser.num_damages--;
2185 DrawLaser(0, DL_LASER_DISABLED);
2186 laser.num_edges = 0;
2188 Bang_MM(laser.start_edge.x, laser.start_edge.y);
2189 Store[x][y] = EL_EMPTY;
2191 else if (IS_MCDUFFIN(Store[x][y]))
2193 game_mm.game_over = TRUE;
2194 game_mm.game_over_cause = GAME_OVER_BOMB;
2195 Store[x][y] = EL_EMPTY;
2198 Feld[x][y] = Store[x][y];
2199 Store[x][y] = Store2[x][y] = 0;
2200 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2202 InitField(x, y, FALSE);
2205 else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2207 int graphic = IMG_MM_DEFAULT_EXPLODING;
2208 int graphic_phase = (phase / delay - 1);
2212 if (Store2[x][y] == EX_KETTLE)
2214 if (graphic_phase < 3)
2216 graphic = IMG_MM_KETTLE_EXPLODING;
2218 else if (graphic_phase < 5)
2224 graphic = IMG_EMPTY;
2228 else if (Store2[x][y] == EX_SHORT)
2230 if (graphic_phase < 4)
2236 graphic = IMG_EMPTY;
2241 getGraphicSource(graphic, graphic_phase, &bitmap, &src_x, &src_y);
2243 BlitBitmap(bitmap, drawto_field, src_x, src_y, TILEX, TILEY,
2244 FX + x * TILEX, FY + y * TILEY);
2246 MarkTileDirty(x, y);
2250 static void Bang_MM(int x, int y)
2252 int element = Feld[x][y];
2253 int mode = EX_NORMAL;
2256 DrawLaser(0, DL_LASER_ENABLED);
2275 if (IS_PACMAN(element))
2276 PlaySoundStereo(SND_QUIEK, ST(x));
2277 else if (element == EL_BOMB || IS_MCDUFFIN(element))
2278 PlaySoundStereo(SND_ROAAAR, ST(x));
2279 else if (element == EL_KEY)
2280 PlaySoundStereo(SND_KLING, ST(x));
2282 PlaySoundStereo((mode == EX_SHORT ? SND_WHOOSH : SND_KABUMM), ST(x));
2284 Explode_MM(x, y, EX_PHASE_START, mode);
2287 void TurnRound(int x, int y)
2299 { 0, 0 }, { 0, 0 }, { 0, 0 },
2304 int left, right, back;
2308 { MV_DOWN, MV_UP, MV_RIGHT },
2309 { MV_UP, MV_DOWN, MV_LEFT },
2311 { MV_LEFT, MV_RIGHT, MV_DOWN },
2312 { 0,0,0 }, { 0,0,0 }, { 0,0,0 },
2313 { MV_RIGHT, MV_LEFT, MV_UP }
2316 int element = Feld[x][y];
2317 int old_move_dir = MovDir[x][y];
2318 int right_dir = turn[old_move_dir].right;
2319 int back_dir = turn[old_move_dir].back;
2320 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
2321 int right_x = x + right_dx, right_y = y + right_dy;
2323 if (element == EL_PACMAN)
2325 boolean can_turn_right = FALSE;
2327 if (IN_LEV_FIELD(right_x, right_y) &&
2328 IS_EATABLE4PACMAN(Feld[right_x][right_y]))
2329 can_turn_right = TRUE;
2332 MovDir[x][y] = right_dir;
2334 MovDir[x][y] = back_dir;
2340 static void StartMoving_MM(int x, int y)
2342 int element = Feld[x][y];
2347 if (CAN_MOVE(element))
2351 if (MovDelay[x][y]) /* wait some time before next movement */
2359 /* now make next step */
2361 Moving2Blocked_MM(x, y, &newx, &newy); /* get next screen position */
2363 if (element == EL_PACMAN &&
2364 IN_LEV_FIELD(newx, newy) && IS_EATABLE4PACMAN(Feld[newx][newy]) &&
2365 !ObjHit(newx, newy, HIT_POS_CENTER))
2367 Store[newx][newy] = Feld[newx][newy];
2368 Feld[newx][newy] = EL_EMPTY;
2370 DrawField_MM(newx, newy);
2372 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy) ||
2373 ObjHit(newx, newy, HIT_POS_CENTER))
2375 /* object was running against a wall */
2382 InitMovingField_MM(x, y, MovDir[x][y]);
2386 ContinueMoving_MM(x, y);
2389 static void ContinueMoving_MM(int x, int y)
2391 int element = Feld[x][y];
2392 int direction = MovDir[x][y];
2393 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2394 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2395 int horiz_move = (dx!=0);
2396 int newx = x + dx, newy = y + dy;
2397 int step = (horiz_move ? dx : dy) * TILEX / 8;
2399 MovPos[x][y] += step;
2401 if (ABS(MovPos[x][y]) >= TILEX) /* object reached its destination */
2403 Feld[x][y] = EL_EMPTY;
2404 Feld[newx][newy] = element;
2406 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2407 MovDelay[newx][newy] = 0;
2409 if (!CAN_MOVE(element))
2410 MovDir[newx][newy] = 0;
2413 DrawField_MM(newx, newy);
2415 Stop[newx][newy] = TRUE;
2417 if (element == EL_PACMAN)
2419 if (Store[newx][newy] == EL_BOMB)
2420 Bang_MM(newx, newy);
2422 if (IS_WALL_AMOEBA(Store[newx][newy]) &&
2423 (LX + 2 * XS) / TILEX == newx &&
2424 (LY + 2 * YS) / TILEY == newy)
2431 else /* still moving on */
2436 laser.redraw = TRUE;
2439 void ClickElement(int mx, int my, int button)
2441 static unsigned int click_delay = 0;
2442 static int click_delay_value = CLICK_DELAY_SHORT;
2443 static boolean new_button = TRUE;
2445 int x = (mx - SX) / TILEX, y = (my - SY) / TILEY;
2447 if (button == MB_RELEASED)
2450 click_delay_value = CLICK_DELAY_SHORT;
2452 /* release eventually hold auto-rotating mirror */
2453 RotateMirror(x, y, MB_RELEASED);
2458 if (!DelayReached(&click_delay, click_delay_value) && !new_button)
2461 if (button == MB_MIDDLEBUTTON) /* middle button has no function */
2464 if (!IN_PIX_FIELD(mx - SX, my - SY))
2467 if (Feld[x][y] == EL_EMPTY)
2470 element = Feld[x][y];
2472 if (IS_MIRROR(element) ||
2473 IS_BEAMER(element) ||
2474 IS_POLAR(element) ||
2475 IS_POLAR_CROSS(element) ||
2476 IS_DF_MIRROR(element) ||
2477 IS_DF_MIRROR_AUTO(element))
2479 RotateMirror(x, y, button);
2481 else if (IS_MCDUFFIN(element))
2483 if (!laser.fuse_off)
2485 DrawLaser(0, DL_LASER_DISABLED);
2492 element = get_rotated_element(element, BUTTON_ROTATION(button));
2493 laser.start_angle = get_element_angle(element);
2497 Feld[x][y] = element;
2504 if (!laser.fuse_off)
2507 else if (element == EL_FUSE_ON && laser.fuse_off)
2509 if (x != laser.fuse_x || y != laser.fuse_y)
2512 laser.fuse_off = FALSE;
2513 laser.fuse_x = laser.fuse_y = -1;
2515 DrawGraphic_MM(x, y, IMG_MM_FUSE_ACTIVE);
2518 else if (element == EL_FUSE_ON && !laser.fuse_off && new_button)
2520 laser.fuse_off = TRUE;
2523 laser.overloaded = FALSE;
2525 DrawLaser(0, DL_LASER_DISABLED);
2526 DrawGraphic_MM(x, y, IMG_MM_FUSE);
2528 else if (element == EL_LIGHTBALL)
2532 DrawLaser(0, DL_LASER_ENABLED);
2535 click_delay_value = (new_button ? CLICK_DELAY_LONG : CLICK_DELAY_SHORT);
2539 void RotateMirror(int x, int y, int button)
2541 static int hold_x = -1, hold_y = -1;
2543 if (button == MB_RELEASED)
2545 /* release eventually hold auto-rotating mirror */
2552 if (IS_MIRROR(Feld[x][y]) ||
2553 IS_POLAR_CROSS(Feld[x][y]) ||
2554 IS_POLAR(Feld[x][y]) ||
2555 IS_BEAMER(Feld[x][y]) ||
2556 IS_DF_MIRROR(Feld[x][y]) ||
2557 IS_GRID_STEEL_AUTO(Feld[x][y]) ||
2558 IS_GRID_WOOD_AUTO(Feld[x][y]))
2560 Feld[x][y] = get_rotated_element(Feld[x][y], BUTTON_ROTATION(button));
2562 else if (IS_DF_MIRROR_AUTO(Feld[x][y]))
2564 if (button == MB_LEFTBUTTON)
2566 /* left mouse button only for manual adjustment, no auto-rotating;
2567 freeze mirror for until mouse button released */
2571 else if (button == MB_RIGHTBUTTON && (hold_x != x || hold_y != y))
2573 Feld[x][y] = get_rotated_element(Feld[x][y], ROTATE_RIGHT);
2577 if (IS_GRID_STEEL_AUTO(Feld[x][y]) || IS_GRID_WOOD_AUTO(Feld[x][y]))
2579 int edge = Hit[x][y];
2585 DrawLaser(edge - 1, DL_LASER_DISABLED);
2589 else if (ObjHit(x, y, HIT_POS_CENTER))
2591 int edge = Hit[x][y];
2595 Error(ERR_WARN, "RotateMirror: inconsistent field Hit[][]!\n");
2599 DrawLaser(edge - 1, DL_LASER_DISABLED);
2606 if (ObjHit(x, y, HIT_POS_EDGE | HIT_POS_BETWEEN))
2611 if ((IS_BEAMER(Feld[x][y]) ||
2612 IS_POLAR(Feld[x][y]) ||
2613 IS_POLAR_CROSS(Feld[x][y])) && x == ELX && y == ELY)
2617 if (IS_BEAMER(Feld[x][y]))
2620 printf("TEST (%d, %d) [%d] [%d]\n",
2622 laser.beamer_edge, laser.beamer[1].num);
2632 DrawLaser(0, DL_LASER_ENABLED);
2636 void AutoRotateMirrors()
2638 static unsigned int rotate_delay = 0;
2641 if (!DelayReached(&rotate_delay, AUTO_ROTATE_DELAY))
2644 for (x = 0; x < lev_fieldx; x++)
2646 for (y = 0; y < lev_fieldy; y++)
2648 int element = Feld[x][y];
2650 if (IS_DF_MIRROR_AUTO(element) ||
2651 IS_GRID_WOOD_AUTO(element) ||
2652 IS_GRID_STEEL_AUTO(element) ||
2653 element == EL_REFRACTOR)
2654 RotateMirror(x, y, MB_RIGHTBUTTON);
2659 boolean ObjHit(int obx, int oby, int bits)
2666 if (bits & HIT_POS_CENTER)
2668 if (ReadPixel(drawto, SX + obx + 15, SY + oby + 15) == pen_ray)
2672 if (bits & HIT_POS_EDGE)
2674 for (i = 0; i < 4; i++)
2675 if (ReadPixel(drawto,
2676 SX + obx + 31 * (i % 2),
2677 SY + oby + 31 * (i / 2)) == pen_ray)
2681 if (bits & HIT_POS_BETWEEN)
2683 for (i = 0; i < 4; i++)
2684 if (ReadPixel(drawto,
2685 SX + 4 + obx + 22 * (i % 2),
2686 SY + 4 + oby + 22 * (i / 2)) == pen_ray)
2693 void DeletePacMan(int px, int py)
2699 if (game_mm.num_pacman <= 1)
2701 game_mm.num_pacman = 0;
2705 for (i = 0; i < game_mm.num_pacman; i++)
2706 if (game_mm.pacman[i].x == px && game_mm.pacman[i].y == py)
2709 game_mm.num_pacman--;
2711 for (j = i; j < game_mm.num_pacman; j++)
2713 game_mm.pacman[j].x = game_mm.pacman[j + 1].x;
2714 game_mm.pacman[j].y = game_mm.pacman[j + 1].y;
2715 game_mm.pacman[j].dir = game_mm.pacman[j + 1].dir;
2719 void ColorCycling(void)
2721 static int CC, Cc = 0;
2723 static int color, old = 0xF00, new = 0x010, mult = 1;
2724 static unsigned short red, green, blue;
2726 if (color_status == STATIC_COLORS)
2731 if (CC < Cc || CC > Cc + 50)
2735 color = old + new * mult;
2741 if (ABS(mult) == 16)
2751 red = 0x0e00 * ((color & 0xF00) >> 8);
2752 green = 0x0e00 * ((color & 0x0F0) >> 4);
2753 blue = 0x0e00 * ((color & 0x00F));
2754 SetRGB(pen_magicolor[0], red, green, blue);
2756 red = 0x1111 * ((color & 0xF00) >> 8);
2757 green = 0x1111 * ((color & 0x0F0) >> 4);
2758 blue = 0x1111 * ((color & 0x00F));
2759 SetRGB(pen_magicolor[1], red, green, blue);
2763 static void GameActions_MM_Ext(byte action[MAX_PLAYERS], boolean warp_mode)
2765 static unsigned int action_delay = 0;
2766 static unsigned int pacman_delay = 0;
2767 static unsigned int energy_delay = 0;
2768 static unsigned int overload_delay = 0;
2774 WaitUntilDelayReached(&action_delay, GameFrameDelay);
2776 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2779 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2781 element = Feld[x][y];
2783 if (!IS_MOVING(x, y) && CAN_MOVE(element))
2784 StartMoving_MM(x, y);
2785 else if (IS_MOVING(x, y))
2786 ContinueMoving_MM(x, y);
2787 else if (IS_EXPLODING(element))
2788 Explode_MM(x, y, Frame[x][y], EX_NORMAL);
2789 else if (element == EL_EXIT_OPENING)
2791 else if (element == EL_GRAY_BALL_OPENING)
2792 OpenSurpriseBall(x, y);
2793 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_ICE)
2795 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_AMOEBA)
2799 AutoRotateMirrors();
2802 /* !!! CHANGE THIS: REDRAW ONLY WHEN NEEDED !!! */
2804 /* redraw after Explode_MM() ... */
2806 DrawLaser(0, DL_LASER_ENABLED);
2807 laser.redraw = FALSE;
2812 if (game_mm.num_pacman && DelayReached(&pacman_delay, 250))
2816 if (laser.num_damages > MAX_LASER_LEN && !laser.fuse_off)
2818 DrawLaser(0, DL_LASER_DISABLED);
2823 if (DelayReached(&energy_delay, 4000))
2825 game_mm.energy_left--;
2826 if (game_mm.energy_left >= 0)
2829 BlitBitmap(pix[PIX_DOOR], drawto,
2830 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
2831 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
2832 DX_ENERGY, DY_ENERGY);
2834 redraw_mask |= REDRAW_DOOR_1;
2836 else if (setup.time_limit)
2840 for (i = 15; i >= 0; i--)
2843 SetRGB(pen_ray, 0x0000, 0x0000, i * color_scale);
2845 pen_ray = GetPixelFromRGB(window,
2846 native_mm_level.laser_red * 0x11 * i,
2847 native_mm_level.laser_green * 0x11 * i,
2848 native_mm_level.laser_blue * 0x11 * i);
2850 DrawLaser(0, DL_LASER_ENABLED);
2855 StopSound(SND_WARNTON);
2858 DrawLaser(0, DL_LASER_DISABLED);
2859 game_mm.game_over = TRUE;
2860 game_mm.game_over_cause = GAME_OVER_NO_ENERGY;
2863 if (Request("Out of magic energy ! Play it again ?",
2864 REQ_ASK | REQ_STAY_CLOSED))
2870 game_status = MAINMENU;
2879 element = laser.dest_element;
2882 if (element != Feld[ELX][ELY])
2884 printf("element == %d, Feld[ELX][ELY] == %d\n",
2885 element, Feld[ELX][ELY]);
2889 if (!laser.overloaded && laser.overload_value == 0 &&
2890 element != EL_BOMB &&
2891 element != EL_MINE &&
2892 element != EL_BALL_GRAY &&
2893 element != EL_BLOCK_STONE &&
2894 element != EL_BLOCK_WOOD &&
2895 element != EL_FUSE_ON &&
2896 element != EL_FUEL_FULL &&
2897 !IS_WALL_ICE(element) &&
2898 !IS_WALL_AMOEBA(element))
2901 if (((laser.overloaded && laser.overload_value < MAX_LASER_OVERLOAD) ||
2902 (!laser.overloaded && laser.overload_value > 0)) &&
2903 DelayReached(&overload_delay, 60 + !laser.overloaded * 120))
2905 if (laser.overloaded)
2906 laser.overload_value++;
2908 laser.overload_value--;
2910 if (game_mm.cheat_no_overload)
2912 laser.overloaded = FALSE;
2913 laser.overload_value = 0;
2916 if (laser.overload_value < MAX_LASER_OVERLOAD - 8)
2918 int color_up = 0xFF * laser.overload_value / MAX_LASER_OVERLOAD;
2919 int color_down = 0xFF - color_up;
2922 SetRGB(pen_ray, (laser.overload_value / 6) * color_scale, 0x0000,
2923 (15 - (laser.overload_value / 6)) * color_scale);
2926 GetPixelFromRGB(window,
2927 (native_mm_level.laser_red ? 0xFF : color_up),
2928 (native_mm_level.laser_green ? color_down : 0x00),
2929 (native_mm_level.laser_blue ? color_down : 0x00));
2931 DrawLaser(0, DL_LASER_ENABLED);
2935 if (laser.overloaded)
2937 if (setup.sound_loops)
2938 PlaySoundExt(SND_WARNTON, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
2939 SND_CTRL_PLAY_LOOP);
2941 PlaySoundStereo(SND_WARNTON, SOUND_MAX_RIGHT);
2944 if (!laser.overloaded)
2945 StopSound(SND_WARNTON);
2947 if (laser.overloaded)
2950 BlitBitmap(pix[PIX_DOOR], drawto,
2951 DOOR_GFX_PAGEX4 + XX_OVERLOAD,
2952 DOOR_GFX_PAGEY1 + YY_OVERLOAD + OVERLOAD_YSIZE
2953 - laser.overload_value,
2954 OVERLOAD_XSIZE, laser.overload_value,
2955 DX_OVERLOAD, DY_OVERLOAD + OVERLOAD_YSIZE
2956 - laser.overload_value);
2958 redraw_mask |= REDRAW_DOOR_1;
2963 BlitBitmap(pix[PIX_DOOR], drawto,
2964 DOOR_GFX_PAGEX5 + XX_OVERLOAD, DOOR_GFX_PAGEY1 + YY_OVERLOAD,
2965 OVERLOAD_XSIZE, OVERLOAD_YSIZE - laser.overload_value,
2966 DX_OVERLOAD, DY_OVERLOAD);
2968 redraw_mask |= REDRAW_DOOR_1;
2971 if (laser.overload_value == MAX_LASER_OVERLOAD)
2975 for (i = 15; i >= 0; i--)
2978 SetRGB(pen_ray, i * color_scale, 0x0000, 0x0000);
2981 pen_ray = GetPixelFromRGB(window, 0x11 * i, 0x00, 0x00);
2983 DrawLaser(0, DL_LASER_ENABLED);
2988 DrawLaser(0, DL_LASER_DISABLED);
2990 game_mm.game_over = TRUE;
2991 game_mm.game_over_cause = GAME_OVER_OVERLOADED;
2994 if (Request("Magic spell hit Mc Duffin ! Play it again ?",
2995 REQ_ASK | REQ_STAY_CLOSED))
3001 game_status = MAINMENU;
3015 if (element == EL_BOMB && CT > 1500)
3017 if (game_mm.cheat_no_explosion)
3021 laser.num_damages--;
3022 DrawLaser(0, DL_LASER_DISABLED);
3023 laser.num_edges = 0;
3028 laser.dest_element = EL_EXPLODING_OPAQUE;
3032 laser.num_damages--;
3033 DrawLaser(0, DL_LASER_DISABLED);
3035 laser.num_edges = 0;
3036 Bang_MM(laser.start_edge.x, laser.start_edge.y);
3038 if (Request("Bomb killed Mc Duffin ! Play it again ?",
3039 REQ_ASK | REQ_STAY_CLOSED))
3045 game_status = MAINMENU;
3053 if (element == EL_FUSE_ON && CT > 500)
3055 laser.fuse_off = TRUE;
3059 DrawLaser(0, DL_LASER_DISABLED);
3060 DrawGraphic_MM(ELX, ELY, IMG_MM_FUSE);
3063 if (element == EL_BALL_GRAY && CT > 1500)
3065 static int new_elements[] =
3068 EL_MIRROR_FIXED_START,
3070 EL_POLAR_CROSS_START,
3076 int num_new_elements = sizeof(new_elements) / sizeof(int);
3077 int new_element = new_elements[RND(num_new_elements)];
3079 Store[ELX][ELY] = new_element + RND(get_num_elements(new_element));
3080 Feld[ELX][ELY] = EL_GRAY_BALL_OPENING;
3082 /* !!! CHECK AGAIN: Laser on Polarizer !!! */
3093 element = EL_MIRROR_START + RND(16);
3099 element = (rnd == 0 ? EL_KETTLE : rnd == 1 ? EL_BOMB : EL_PRISM);
3106 element = (rnd == 0 ? EL_FUSE_ON :
3107 rnd >= 1 && rnd <= 4 ? EL_PACMAN_RIGHT + rnd - 1 :
3108 rnd >= 5 && rnd <= 20 ? EL_POLAR_START + rnd - 5 :
3109 rnd >= 21 && rnd <= 24 ? EL_POLAR_CROSS_START + rnd - 21 :
3110 EL_MIRROR_FIXED_START + rnd - 25);
3115 graphic = el2gfx(element);
3117 for (i = 0; i < 50; i++)
3123 BlitBitmap(pix[PIX_BACK], drawto,
3124 SX + (graphic % GFX_PER_LINE) * TILEX + x,
3125 SY + (graphic / GFX_PER_LINE) * TILEY + y, 6, 6,
3126 SX + ELX * TILEX + x,
3127 SY + ELY * TILEY + y);
3129 MarkTileDirty(ELX, ELY);
3132 DrawLaser(0, DL_LASER_ENABLED);
3137 Feld[ELX][ELY] = element;
3138 DrawField_MM(ELX, ELY);
3141 printf("NEW ELEMENT: (%d, %d)\n", ELX, ELY);
3144 /* above stuff: GRAY BALL -> PRISM !!! */
3146 LX = ELX * TILEX + 14;
3147 LY = ELY * TILEY + 14;
3148 if (laser.current_angle == (laser.current_angle >> 1) << 1)
3155 laser.num_edges -= 2;
3156 laser.num_damages--;
3160 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
3161 if (laser.damage[i].is_mirror)
3165 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3167 DrawLaser(0, DL_LASER_DISABLED);
3169 DrawLaser(0, DL_LASER_DISABLED);
3175 printf("TEST ELEMENT: %d\n", Feld[0][0]);
3182 if (IS_WALL_ICE(element) && CT > 1000)
3184 PlaySoundStereo(SND_SLURP, ST(ELX));
3187 Feld[ELX][ELY] = Feld[ELX][ELY] - EL_WALL_ICE + EL_WALL_CHANGING;
3188 Store[ELX][ELY] = EL_WALL_ICE;
3189 Store2[ELX][ELY] = laser.wall_mask;
3191 laser.dest_element = Feld[ELX][ELY];
3196 for (i = 0; i < 5; i++)
3202 Feld[ELX][ELY] &= (laser.wall_mask ^ 0xFF);
3206 DrawWallsAnimation_MM(ELX, ELY, Feld[ELX][ELY], phase, laser.wall_mask);
3211 if (Feld[ELX][ELY] == EL_WALL_ICE)
3212 Feld[ELX][ELY] = EL_EMPTY;
3216 LX = laser.edge[laser.num_edges].x - (SX + 2);
3217 LY = laser.edge[laser.num_edges].y - (SY + 2);
3220 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
3221 if (laser.damage[i].is_mirror)
3225 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3227 DrawLaser(0, DL_LASER_DISABLED);
3234 if (IS_WALL_AMOEBA(element) && CT > 1200)
3236 int k1, k2, k3, dx, dy, de, dm;
3237 int element2 = Feld[ELX][ELY];
3239 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element))
3242 for (i = laser.num_damages - 1; i >= 0; i--)
3243 if (laser.damage[i].is_mirror)
3246 r = laser.num_edges;
3247 d = laser.num_damages;
3254 DrawLaser(laser.damage[k1].edge - 1, DL_LASER_DISABLED);
3257 DrawLaser(0, DL_LASER_ENABLED);
3260 x = laser.damage[k1].x;
3261 y = laser.damage[k1].y;
3266 for (i = 0; i < 4; i++)
3268 if (laser.wall_mask & (1 << i))
3270 if (ReadPixel(drawto,
3271 SX + ELX * TILEX + 14 + (i % 2) * 2,
3272 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3274 if (ReadPixel(drawto,
3275 SX + ELX * TILEX + 31 * (i % 2),
3276 SY + ELY * TILEY + 14 + (i / 2) * 2) == pen_ray)
3283 for (i = 0; i < 4; i++)
3285 if (laser.wall_mask & (1 << i))
3287 if (ReadPixel(drawto,
3288 SX + ELX * TILEX + 31 * (i % 2),
3289 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3296 if (laser.num_beamers > 0 ||
3297 k1 < 1 || k2 < 4 || k3 < 4 ||
3298 ReadPixel(drawto, SX + ELX * TILEX + 14, SY + ELY * TILEY + 14)
3301 laser.num_edges = r;
3302 laser.num_damages = d;
3304 DrawLaser(0, DL_LASER_DISABLED);
3307 Feld[ELX][ELY] = element | laser.wall_mask;
3311 de = Feld[ELX][ELY];
3312 dm = laser.wall_mask;
3316 int x = ELX, y = ELY;
3317 int wall_mask = laser.wall_mask;
3320 DrawLaser(0, DL_LASER_ENABLED);
3322 PlaySoundStereo(SND_AMOEBE, ST(dx));
3324 Feld[x][y] = Feld[x][y] - EL_WALL_AMOEBA + EL_WALL_CHANGING;
3325 Store[x][y] = EL_WALL_AMOEBA;
3326 Store2[x][y] = wall_mask;
3332 DrawWallsAnimation_MM(dx, dy, de, 4, dm);
3334 DrawLaser(0, DL_LASER_ENABLED);
3336 PlaySoundStereo(SND_AMOEBE, ST(dx));
3338 for (i = 4; i >= 0; i--)
3340 DrawWallsAnimation_MM(dx, dy, de, i, dm);
3346 DrawLaser(0, DL_LASER_ENABLED);
3351 if ((element == EL_BLOCK_WOOD || element == EL_BLOCK_STONE) &&
3352 laser.stops_inside_element && CT > 1500)
3357 if (ABS(XS) > ABS(YS))
3364 for (i = 0; i < 4; i++)
3371 x = ELX + Step[k * 4].x;
3372 y = ELY + Step[k * 4].y;
3374 if (!IN_LEV_FIELD(x, y) || Feld[x][y] != EL_EMPTY)
3377 if (ObjHit(x, y, HIT_POS_CENTER | HIT_POS_EDGE | HIT_POS_BETWEEN))
3385 laser.overloaded = (element == EL_BLOCK_STONE);
3390 PlaySoundStereo(SND_BONG, ST(ELX));
3393 Feld[x][y] = element;
3395 DrawGraphic_MM(ELX, ELY, IMG_EMPTY);
3398 if (element == EL_BLOCK_STONE && Box[ELX][ELY])
3400 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
3401 DrawLaser(laser.num_edges - 1, DL_LASER_ENABLED);
3409 if (element == EL_FUEL_FULL && CT > 200)
3411 for (i = game_mm.energy_left; i <= MAX_LASER_ENERGY; i+=2)
3414 BlitBitmap(pix[PIX_DOOR], drawto,
3415 DOOR_GFX_PAGEX4 + XX_ENERGY,
3416 DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
3417 ENERGY_XSIZE, i, DX_ENERGY,
3418 DY_ENERGY + ENERGY_YSIZE - i);
3421 redraw_mask |= REDRAW_DOOR_1;
3427 game_mm.energy_left = MAX_LASER_ENERGY;
3428 Feld[ELX][ELY] = EL_FUEL_EMPTY;
3429 DrawField_MM(ELX, ELY);
3431 DrawLaser(0, DL_LASER_ENABLED);
3439 void GameActions_MM(byte action[MAX_PLAYERS], boolean warp_mode)
3442 ClickElement(0, 0, MB_NOT_PRESSED);
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]);