1 // ============================================================================
2 // Mirror Magic -- McDuffin's Revenge
3 // ----------------------------------------------------------------------------
4 // (c) 1994-2017 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
18 /* graphic position values for game controls */
19 #define ENERGY_XSIZE 32
20 #define ENERGY_YSIZE MAX_LASER_ENERGY
21 #define OVERLOAD_XSIZE ENERGY_XSIZE
22 #define OVERLOAD_YSIZE MAX_LASER_OVERLOAD
24 /* values for Explode_MM() */
25 #define EX_PHASE_START 0
30 /* special positions in the game control window (relative to control window) */
39 #define XX_OVERLOAD 60
40 #define YY_OVERLOAD YY_ENERGY
42 /* special positions in the game control window (relative to main window) */
43 #define DX_LEVEL (DX + XX_LEVEL)
44 #define DY_LEVEL (DY + YY_LEVEL)
45 #define DX_KETTLES (DX + XX_KETTLES)
46 #define DY_KETTLES (DY + YY_KETTLES)
47 #define DX_SCORE (DX + XX_SCORE)
48 #define DY_SCORE (DY + YY_SCORE)
49 #define DX_ENERGY (DX + XX_ENERGY)
50 #define DY_ENERGY (DY + YY_ENERGY)
51 #define DX_OVERLOAD (DX + XX_OVERLOAD)
52 #define DY_OVERLOAD (DY + YY_OVERLOAD)
54 #define IS_LOOP_SOUND(s) ((s) == SND_FUEL)
55 #define IS_MUSIC_SOUND(s) ((s) == SND_TYGER || (s) == SND_VOYAGER)
57 /* game button identifiers */
58 #define GAME_CTRL_ID_LEFT 0
59 #define GAME_CTRL_ID_MIDDLE 1
60 #define GAME_CTRL_ID_RIGHT 2
62 #define NUM_GAME_BUTTONS 3
64 /* values for DrawLaser() */
65 #define DL_LASER_DISABLED 0
66 #define DL_LASER_ENABLED 1
68 /* values for 'click_delay_value' in ClickElement() */
69 #define CLICK_DELAY_SHORT 125
70 #define CLICK_DELAY_LONG 250
71 #define AUTO_ROTATE_DELAY CLICK_DELAY_SHORT
73 /* forward declaration for internal use */
74 static int MovingOrBlocked2Element_MM(int, int);
75 static void Bang_MM(int, int);
76 static void RaiseScore_MM(int);
77 static void RemoveMovingField_MM(int, int);
78 static void InitMovingField_MM(int, int, int);
79 static void ContinueMoving_MM(int, int);
80 static void Moving2Blocked_MM(int, int, int *, int *);
83 static int get_element_angle(int element)
85 int element_phase = get_element_phase(element);
87 if (IS_MIRROR_FIXED(element) ||
88 IS_MCDUFFIN(element) ||
91 return 4 * element_phase;
96 static int get_opposite_angle(int angle)
98 int opposite_angle = angle + ANG_RAY_180;
100 /* make sure "opposite_angle" is in valid interval [0, 15] */
101 return (opposite_angle + 16) % 16;
104 static int get_mirrored_angle(int laser_angle, int mirror_angle)
106 int reflected_angle = 16 - laser_angle + mirror_angle;
108 /* make sure "reflected_angle" is in valid interval [0, 15] */
109 return (reflected_angle + 16) % 16;
112 static void InitMovDir_MM(int x, int y)
114 int element = Feld[x][y];
115 static int direction[3][4] =
117 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
118 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
119 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
124 case EL_PACMAN_RIGHT:
128 Feld[x][y] = EL_PACMAN;
129 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
137 static void InitField(int x, int y, boolean init_game)
139 int element = Feld[x][y];
144 Feld[x][y] = EL_EMPTY;
149 if (native_mm_level.auto_count_kettles)
150 game_mm.kettles_still_needed++;
153 case EL_LIGHTBULB_OFF:
154 game_mm.lights_still_needed++;
158 if (IS_MIRROR(element) ||
159 IS_BEAMER_OLD(element) ||
160 IS_BEAMER(element) ||
162 IS_POLAR_CROSS(element) ||
163 IS_DF_MIRROR(element) ||
164 IS_DF_MIRROR_AUTO(element) ||
165 IS_GRID_STEEL_AUTO(element) ||
166 IS_GRID_WOOD_AUTO(element) ||
167 IS_FIBRE_OPTIC(element))
169 if (IS_BEAMER_OLD(element))
171 Feld[x][y] = EL_BEAMER_BLUE_START + (element - EL_BEAMER_START);
172 element = Feld[x][y];
175 if (!IS_FIBRE_OPTIC(element))
177 static int steps_grid_auto = 0;
179 if (game_mm.num_cycle == 0) /* initialize cycle steps for grids */
180 steps_grid_auto = RND(16) * (RND(2) ? -1 : +1);
182 if (IS_GRID_STEEL_AUTO(element) ||
183 IS_GRID_WOOD_AUTO(element))
184 game_mm.cycle[game_mm.num_cycle].steps = steps_grid_auto;
186 game_mm.cycle[game_mm.num_cycle].steps = RND(16) * (RND(2) ? -1 : +1);
188 game_mm.cycle[game_mm.num_cycle].x = x;
189 game_mm.cycle[game_mm.num_cycle].y = y;
193 if (IS_BEAMER(element) || IS_FIBRE_OPTIC(element))
195 int beamer_nr = BEAMER_NR(element);
196 int nr = laser.beamer[beamer_nr][0].num;
198 laser.beamer[beamer_nr][nr].x = x;
199 laser.beamer[beamer_nr][nr].y = y;
200 laser.beamer[beamer_nr][nr].num = 1;
203 else if (IS_PACMAN(element))
207 else if (IS_MCDUFFIN(element) || IS_LASER(element))
209 laser.start_edge.x = x;
210 laser.start_edge.y = y;
211 laser.start_angle = get_element_angle(element);
218 static void InitCycleElements()
222 if (game_mm.num_cycle == 0) /* no elements to cycle */
225 for (i = 0; i < 16; i++)
227 for (j = 0; j < game_mm.num_cycle; j++)
229 int x = game_mm.cycle[j].x;
230 int y = game_mm.cycle[j].y;
231 int step = SIGN(game_mm.cycle[j].steps);
232 int last_element = Feld[x][y];
233 int next_element = get_rotated_element(last_element, step);
235 if (!game_mm.cycle[j].steps)
238 Feld[x][y] = next_element;
241 game_mm.cycle[j].steps -= step;
248 if (setup.quick_doors)
252 Delay(AUTO_ROTATE_DELAY);
256 static void InitLaser()
258 int start_element = Feld[laser.start_edge.x][laser.start_edge.y];
259 int step = (IS_LASER(start_element) ? 4 : 0);
261 LX = laser.start_edge.x * TILEX;
262 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
265 LX += (laser.start_angle == ANG_RAY_RIGHT ? 28 + step : 0 - step);
267 LY = laser.start_edge.y * TILEY;
268 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
269 LY += (laser.start_angle == ANG_RAY_DOWN ? 28 + step : 0 - step);
273 XS = 2 * Step[laser.start_angle].x;
274 YS = 2 * Step[laser.start_angle].y;
276 laser.current_angle = laser.start_angle;
278 laser.num_damages = 0;
280 laser.num_beamers = 0;
281 laser.beamer_edge[0] = 0;
283 AddLaserEdge(LX, LY); /* set laser starting edge */
285 pen_ray = GetPixelFromRGB(window,
286 native_mm_level.laser_red * 0xFF,
287 native_mm_level.laser_green * 0xFF,
288 native_mm_level.laser_blue * 0xFF);
291 void InitGameEngine_MM()
295 /* set global game control values */
296 game_mm.num_cycle = 0;
297 game_mm.num_pacman = 0;
300 game_mm.energy_left = native_mm_level.time;
301 game_mm.kettles_still_needed =
302 (native_mm_level.auto_count_kettles ? 0 : native_mm_level.kettles_needed);
303 game_mm.lights_still_needed = 0;
304 game_mm.num_keys = 0;
306 game_mm.level_solved = FALSE;
307 game_mm.game_over = FALSE;
308 game_mm.game_over_cause = 0;
310 /* set global laser control values (must be set before "InitLaser()") */
311 laser.start_edge.x = 0;
312 laser.start_edge.y = 0;
313 laser.start_angle = 0;
315 for (i = 0; i < MAX_NUM_BEAMERS; i++)
316 laser.beamer[i][0].num = laser.beamer[i][1].num = 0;
318 laser.overloaded = FALSE;
319 laser.overload_value = 0;
320 laser.fuse_off = FALSE;
321 laser.fuse_x = laser.fuse_y = -1;
323 laser.dest_element = EL_EMPTY;
328 for (x = 0; x < lev_fieldx; x++)
330 for (y = 0; y < lev_fieldy; y++)
332 Feld[x][y] = Ur[x][y];
333 Hit[x][y] = Box[x][y] = 0;
335 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
336 Store[x][y] = Store2[x][y] = 0;
340 InitField(x, y, TRUE);
345 CloseDoor(DOOR_CLOSE_1);
351 void InitGameEngine_MM_AfterFadingIn()
357 /* copy default game door content to main double buffer */
358 BlitBitmap(pix[PIX_DOOR], drawto,
359 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
363 DrawText(DX_LEVEL, DY_LEVEL,
364 int2str(level_nr, 2), FONT_TEXT_2);
365 DrawText(DX_KETTLES, DY_KETTLES,
366 int2str(game_mm.kettles_still_needed, 3), FONT_TEXT_2);
367 DrawText(DX_SCORE, DY_SCORE,
368 int2str(game_mm.score, 4), FONT_TEXT_2);
377 /* copy actual game door content to door double buffer for OpenDoor() */
378 BlitBitmap(drawto, pix[PIX_DB_DOOR],
379 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
383 OpenDoor(DOOR_OPEN_ALL);
386 if (setup.sound_loops)
387 PlaySoundExt(SND_FUEL, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT, SND_CTRL_PLAY_LOOP);
389 #if 0 // !!! TEMPORARILY DISABLED !!!
390 for (i = 0; i <= game_mm.energy_left; i += 2)
392 if (!setup.sound_loops)
393 PlaySoundStereo(SND_FUEL, SOUND_MAX_RIGHT);
396 BlitBitmap(pix[PIX_DOOR], drawto,
397 DOOR_GFX_PAGEX4 + XX_ENERGY,
398 DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
400 DX_ENERGY, DY_ENERGY + ENERGY_YSIZE - i);
403 redraw_mask |= REDRAW_DOOR_1;
409 if (setup.quick_doors)
416 if (setup.sound_loops)
421 if (setup.sound_music && num_bg_loops)
422 PlayMusic(level_nr % num_bg_loops);
428 void AddLaserEdge(int lx, int ly)
430 if (lx < -2 || ly < -2 || lx >= SXSIZE + 2 || ly >= SYSIZE + 2)
432 Error(ERR_WARN, "AddLaserEdge: out of bounds: %d, %d", lx, ly);
437 laser.edge[laser.num_edges].x = SX + 2 + lx;
438 laser.edge[laser.num_edges].y = SY + 2 + ly;
444 void AddDamagedField(int ex, int ey)
446 laser.damage[laser.num_damages].is_mirror = FALSE;
447 laser.damage[laser.num_damages].angle = laser.current_angle;
448 laser.damage[laser.num_damages].edge = laser.num_edges;
449 laser.damage[laser.num_damages].x = ex;
450 laser.damage[laser.num_damages].y = ey;
460 int last_x = laser.edge[laser.num_edges - 1].x - SX - 2;
461 int last_y = laser.edge[laser.num_edges - 1].y - SY - 2;
463 return ((x - last_x) * XS < 0 || (y - last_y) * YS < 0);
469 static int getMaskFromElement(int element)
471 if (IS_GRID(element))
472 return IMG_MM_MASK_GRID_1 + get_element_phase(element);
473 else if (IS_MCDUFFIN(element))
474 return IMG_MM_MASK_MCDUFFIN_RIGHT + get_element_phase(element);
475 else if (IS_RECTANGLE(element) || IS_DF_GRID(element))
476 return IMG_MM_MASK_RECTANGLE;
478 return IMG_MM_MASK_CIRCLE;
486 printf("ScanPixel: start scanning at (%d, %d) [%d, %d] [%d, %d]\n",
487 LX, LY, LX / TILEX, LY / TILEY, LX % TILEX, LY % TILEY);
490 /* follow laser beam until it hits something (at least the screen border) */
491 while (hit_mask == HIT_MASK_NO_HIT)
497 if (SX + LX < REAL_SX || SX + LX >= REAL_SX + FULL_SXSIZE ||
498 SY + LY < REAL_SY || SY + LY >= REAL_SY + FULL_SYSIZE)
500 printf("ScanPixel: touched screen border!\n");
506 for (i = 0; i < 4; i++)
508 int px = LX + (i % 2) * 2;
509 int py = LY + (i / 2) * 2;
512 int lx = (px + TILEX) / TILEX - 1; /* ...+TILEX...-1 to get correct */
513 int ly = (py + TILEY) / TILEY - 1; /* negative values! */
516 if (IN_LEV_FIELD(lx, ly))
518 int element = Feld[lx][ly];
520 if (element == EL_EMPTY || element == EL_EXPLODING_TRANSP)
524 else if (IS_WALL(element) || IS_WALL_CHANGING(element))
526 int pos = dy / MINI_TILEY * 2 + dx / MINI_TILEX;
528 pixel = ((element & (1 << pos)) ? 1 : 0);
532 int graphic_mask = getMaskFromElement(element);
537 getGraphicSource(graphic_mask, 0, &bitmap, &src_x, &src_y);
542 pixel = (ReadPixel(bitmap, mask_x, mask_y) ? 1 : 0);
547 pixel = (SX + px < REAL_SX || SX + px >= REAL_SX + FULL_SXSIZE ||
548 SY + py < REAL_SY || SY + py >= REAL_SY + FULL_SYSIZE);
551 if ((Sign[laser.current_angle] & (1 << i)) && pixel)
552 hit_mask |= (1 << i);
555 if (hit_mask == HIT_MASK_NO_HIT)
557 /* hit nothing -- go on with another step */
569 int end = 0, rf = laser.num_edges;
571 /* do not scan laser again after the game was lost for whatever reason */
572 if (game_mm.game_over)
575 laser.overloaded = FALSE;
576 laser.stops_inside_element = FALSE;
578 DrawLaser(0, DL_LASER_ENABLED);
581 printf("Start scanning with LX == %d, LY == %d, XS == %d, YS == %d\n",
589 if (laser.num_edges > MAX_LASER_LEN || laser.num_damages > MAX_LASER_LEN)
592 laser.overloaded = TRUE;
597 hit_mask = ScanPixel();
600 printf("Hit something at LX == %d, LY == %d, XS == %d, YS == %d\n",
604 /* hit something -- check out what it was */
605 ELX = (LX + XS) / TILEX;
606 ELY = (LY + YS) / TILEY;
609 printf("hit_mask (1) == '%x' (%d, %d) (%d, %d)\n",
610 hit_mask, LX, LY, ELX, ELY);
613 if (!IN_LEV_FIELD(ELX, ELY) || !IN_PIX_FIELD(LX, LY))
616 laser.dest_element = element;
621 if (hit_mask == (HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT))
623 /* we have hit the top-right and bottom-left element --
624 choose the bottom-left one */
625 /* !!! THIS CAN BE DONE MORE INTELLIGENTLY, FOR EXAMPLE, IF ONE
626 ELEMENT WAS STEEL AND THE OTHER ONE WAS ICE => ALWAYS CHOOSE
627 THE ICE AND MELT IT AWAY INSTEAD OF OVERLOADING LASER !!! */
628 ELX = (LX - 2) / TILEX;
629 ELY = (LY + 2) / TILEY;
632 if (hit_mask == (HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT))
634 /* we have hit the top-left and bottom-right element --
635 choose the top-left one */
636 /* !!! SEE ABOVE !!! */
637 ELX = (LX - 2) / TILEX;
638 ELY = (LY - 2) / TILEY;
642 printf("hit_mask (2) == '%x' (%d, %d) (%d, %d)\n",
643 hit_mask, LX, LY, ELX, ELY);
646 element = Feld[ELX][ELY];
647 laser.dest_element = element;
650 printf("Hit element %d at (%d, %d) [%d, %d] [%d, %d] [%d]\n",
653 LX % TILEX, LY % TILEY,
658 if (!IN_LEV_FIELD(ELX, ELY))
659 printf("WARNING! (1) %d, %d (%d)\n", ELX, ELY, element);
662 if (element == EL_EMPTY)
664 if (!HitOnlyAnEdge(element, hit_mask))
667 else if (element == EL_FUSE_ON)
669 if (HitPolarizer(element, hit_mask))
672 else if (IS_GRID(element) || IS_DF_GRID(element))
674 if (HitPolarizer(element, hit_mask))
677 else if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD ||
678 element == EL_GATE_STONE || element == EL_GATE_WOOD)
680 if (HitBlock(element, hit_mask))
687 else if (IS_MCDUFFIN(element))
689 if (HitLaserSource(element, hit_mask))
692 else if ((element >= EL_EXIT_CLOSED && element <= EL_EXIT_OPEN) ||
693 IS_RECEIVER(element))
695 if (HitLaserDestination(element, hit_mask))
698 else if (IS_WALL(element))
700 if (IS_WALL_STEEL(element) || IS_DF_WALL_STEEL(element))
702 if (HitReflectingWalls(element, hit_mask))
707 if (HitAbsorbingWalls(element, hit_mask))
713 if (HitElement(element, hit_mask))
718 DrawLaser(rf - 1, DL_LASER_ENABLED);
719 rf = laser.num_edges;
723 if (laser.dest_element != Feld[ELX][ELY])
725 printf("ALARM: laser.dest_element == %d, Feld[ELX][ELY] == %d\n",
726 laser.dest_element, Feld[ELX][ELY]);
730 if (!end && !laser.stops_inside_element && !StepBehind())
733 printf("ScanLaser: Go one step back\n");
739 AddLaserEdge(LX, LY);
743 DrawLaser(rf - 1, DL_LASER_ENABLED);
748 if (!IN_LEV_FIELD(ELX, ELY))
749 printf("WARNING! (2) %d, %d\n", ELX, ELY);
753 void DrawLaserExt(int start_edge, int num_edges, int mode)
759 printf("DrawLaserExt: start_edge, num_edges, mode == %d, %d, %d\n",
760 start_edge, num_edges, mode);
765 Error(ERR_WARN, "DrawLaserExt: start_edge < 0");
772 Error(ERR_WARN, "DrawLaserExt: num_edges < 0");
778 if (mode == DL_LASER_DISABLED)
780 printf("DrawLaser: Delete laser from edge %d\n", start_edge);
784 /* now draw the laser to the backbuffer and (if enabled) to the screen */
785 DrawLines(drawto, &laser.edge[start_edge], num_edges,
786 (mode == DL_LASER_ENABLED ? pen_ray : pen_bg));
788 redraw_mask |= REDRAW_FIELD;
790 if (mode == DL_LASER_ENABLED)
793 /* after the laser was deleted, the "damaged" graphics must be restored */
794 if (laser.num_damages)
796 int damage_start = 0;
799 /* determine the starting edge, from which graphics need to be restored */
802 for (i = 0; i < laser.num_damages; i++)
804 if (laser.damage[i].edge == start_edge + 1)
813 /* restore graphics from this starting edge to the end of damage list */
814 for (i = damage_start; i < laser.num_damages; i++)
816 int lx = laser.damage[i].x;
817 int ly = laser.damage[i].y;
818 int element = Feld[lx][ly];
820 if (Hit[lx][ly] == laser.damage[i].edge)
821 if (!((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
824 if (Box[lx][ly] == laser.damage[i].edge)
827 if (IS_DRAWABLE(element))
828 DrawField_MM(lx, ly);
831 elx = laser.damage[damage_start].x;
832 ely = laser.damage[damage_start].y;
833 element = Feld[elx][ely];
836 if (IS_BEAMER(element))
840 for (i = 0; i < laser.num_beamers; i++)
841 printf("-> %d\n", laser.beamer_edge[i]);
842 printf("DrawLaserExt: IS_BEAMER: [%d]: Hit[%d][%d] == %d [%d]\n",
843 mode, elx, ely, Hit[elx][ely], start_edge);
844 printf("DrawLaserExt: IS_BEAMER: %d / %d\n",
845 get_element_angle(element), laser.damage[damage_start].angle);
849 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
850 laser.num_beamers > 0 &&
851 start_edge == laser.beamer_edge[laser.num_beamers - 1])
853 /* element is outgoing beamer */
854 laser.num_damages = damage_start + 1;
856 if (IS_BEAMER(element))
857 laser.current_angle = get_element_angle(element);
861 /* element is incoming beamer or other element */
862 laser.num_damages = damage_start;
863 laser.current_angle = laser.damage[laser.num_damages].angle;
868 /* no damages but McDuffin himself (who needs to be redrawn anyway) */
870 elx = laser.start_edge.x;
871 ely = laser.start_edge.y;
872 element = Feld[elx][ely];
875 laser.num_edges = start_edge + 1;
877 laser.current_angle = laser.start_angle;
879 LX = laser.edge[start_edge].x - (SX + 2);
880 LY = laser.edge[start_edge].y - (SY + 2);
881 XS = 2 * Step[laser.current_angle].x;
882 YS = 2 * Step[laser.current_angle].y;
885 printf("DrawLaser: Set (LX, LY) to (%d, %d) [%d]\n",
891 if (IS_BEAMER(element) ||
892 IS_FIBRE_OPTIC(element) ||
893 IS_PACMAN(element) ||
895 IS_POLAR_CROSS(element) ||
896 element == EL_FUSE_ON)
901 printf("element == %d\n", element);
904 if (IS_22_5_ANGLE(laser.current_angle)) /* neither 90° nor 45° angle */
905 step_size = ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) ? 4 : 3);
909 if (IS_POLAR(element) || IS_POLAR_CROSS(element) ||
910 ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
911 (laser.num_beamers == 0 ||
912 start_edge != laser.beamer_edge[laser.num_beamers - 1])))
914 /* element is incoming beamer or other element */
915 step_size = -step_size;
920 if (IS_BEAMER(element))
922 printf("start_edge == %d, laser.beamer_edge == %d\n",
923 start_edge, laser.beamer_edge);
927 LX += step_size * XS;
928 LY += step_size * YS;
930 else if (element != EL_EMPTY)
939 printf("DrawLaser: Finally: (LX, LY) to (%d, %d) [%d]\n",
944 void DrawLaser(int start_edge, int mode)
946 if (laser.num_edges - start_edge < 0)
948 Error(ERR_WARN, "DrawLaser: laser.num_edges - start_edge < 0");
953 /* check if laser is interrupted by beamer element */
954 if (laser.num_beamers > 0 &&
955 start_edge < laser.beamer_edge[laser.num_beamers - 1])
957 if (mode == DL_LASER_ENABLED)
960 int tmp_start_edge = start_edge;
962 /* draw laser segments forward from the start to the last beamer */
963 for (i = 0; i < laser.num_beamers; i++)
965 int tmp_num_edges = laser.beamer_edge[i] - tmp_start_edge;
967 if (tmp_num_edges <= 0)
971 printf("DrawLaser: DL_LASER_ENABLED: i==%d: %d, %d\n",
972 i, laser.beamer_edge[i], tmp_start_edge);
975 DrawLaserExt(tmp_start_edge, tmp_num_edges, DL_LASER_ENABLED);
977 tmp_start_edge = laser.beamer_edge[i];
980 /* draw last segment from last beamer to the end */
981 DrawLaserExt(tmp_start_edge, laser.num_edges - tmp_start_edge,
987 int last_num_edges = laser.num_edges;
988 int num_beamers = laser.num_beamers;
990 /* delete laser segments backward from the end to the first beamer */
991 for (i = num_beamers-1; i >= 0; i--)
993 int tmp_num_edges = last_num_edges - laser.beamer_edge[i];
995 if (laser.beamer_edge[i] - start_edge <= 0)
998 DrawLaserExt(laser.beamer_edge[i], tmp_num_edges, DL_LASER_DISABLED);
1000 last_num_edges = laser.beamer_edge[i];
1001 laser.num_beamers--;
1005 if (last_num_edges - start_edge <= 0)
1006 printf("DrawLaser: DL_LASER_DISABLED: %d, %d\n",
1007 last_num_edges, start_edge);
1010 /* delete first segment from start to the first beamer */
1011 DrawLaserExt(start_edge, last_num_edges - start_edge, DL_LASER_DISABLED);
1016 DrawLaserExt(start_edge, laser.num_edges - start_edge, mode);
1020 boolean HitElement(int element, int hit_mask)
1022 if (HitOnlyAnEdge(element, hit_mask))
1025 if (IS_MOVING(ELX, ELY) || IS_BLOCKED(ELX, ELY))
1026 element = MovingOrBlocked2Element_MM(ELX, ELY);
1029 printf("HitElement (1): element == %d\n", element);
1033 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1034 printf("HitElement (%d): EXACT MATCH @ (%d, %d)\n", element, ELX, ELY);
1036 printf("HitElement (%d): FUZZY MATCH @ (%d, %d)\n", element, ELX, ELY);
1039 AddDamagedField(ELX, ELY);
1041 /* this is more precise: check if laser would go through the center */
1042 if ((ELX * TILEX + 14 - LX) * YS != (ELY * TILEY + 14 - LY) * XS)
1044 /* skip the whole element before continuing the scan */
1050 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1052 if (LX/TILEX > ELX || LY/TILEY > ELY)
1054 /* skipping scan positions to the right and down skips one scan
1055 position too much, because this is only the top left scan position
1056 of totally four scan positions (plus one to the right, one to the
1057 bottom and one to the bottom right) */
1067 printf("HitElement (2): element == %d\n", element);
1070 if (LX + 5 * XS < 0 ||
1080 printf("HitElement (3): element == %d\n", element);
1083 if (IS_POLAR(element) &&
1084 ((element - EL_POLAR_START) % 2 ||
1085 (element - EL_POLAR_START) / 2 != laser.current_angle % 8))
1087 PlaySoundStereo(SND_KINK, ST(ELX));
1089 laser.num_damages--;
1094 if (IS_POLAR_CROSS(element) &&
1095 (element - EL_POLAR_CROSS_START) != laser.current_angle % 4)
1097 PlaySoundStereo(SND_KINK, ST(ELX));
1099 laser.num_damages--;
1104 if (!IS_BEAMER(element) &&
1105 !IS_FIBRE_OPTIC(element) &&
1106 !IS_GRID_WOOD(element) &&
1107 element != EL_FUEL_EMPTY)
1110 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1111 printf("EXACT MATCH @ (%d, %d)\n", ELX, ELY);
1113 printf("FUZZY MATCH @ (%d, %d)\n", ELX, ELY);
1116 LX = ELX * TILEX + 14;
1117 LY = ELY * TILEY + 14;
1119 AddLaserEdge(LX, LY);
1122 if (IS_MIRROR(element) ||
1123 IS_MIRROR_FIXED(element) ||
1124 IS_POLAR(element) ||
1125 IS_POLAR_CROSS(element) ||
1126 IS_DF_MIRROR(element) ||
1127 IS_DF_MIRROR_AUTO(element) ||
1128 element == EL_PRISM ||
1129 element == EL_REFRACTOR)
1131 int current_angle = laser.current_angle;
1134 laser.num_damages--;
1136 AddDamagedField(ELX, ELY);
1138 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1141 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1143 if (IS_MIRROR(element) ||
1144 IS_MIRROR_FIXED(element) ||
1145 IS_DF_MIRROR(element) ||
1146 IS_DF_MIRROR_AUTO(element))
1147 laser.current_angle = get_mirrored_angle(laser.current_angle,
1148 get_element_angle(element));
1150 if (element == EL_PRISM || element == EL_REFRACTOR)
1151 laser.current_angle = RND(16);
1153 XS = 2 * Step[laser.current_angle].x;
1154 YS = 2 * Step[laser.current_angle].y;
1156 if (!IS_22_5_ANGLE(laser.current_angle)) /* 90° or 45° angle */
1161 LX += step_size * XS;
1162 LY += step_size * YS;
1165 /* draw sparkles on mirror */
1166 if ((IS_MIRROR(element) || IS_MIRROR_FIXED(element)) &&
1167 current_angle != laser.current_angle)
1169 MoveSprite(vp, &Pfeil[2], 4 + 16 * ELX, 5 + 16 * ELY + 1);
1173 if ((!IS_POLAR(element) && !IS_POLAR_CROSS(element)) &&
1174 current_angle != laser.current_angle)
1175 PlaySoundStereo(SND_LASER, ST(ELX));
1178 (get_opposite_angle(laser.current_angle) ==
1179 laser.damage[laser.num_damages - 1].angle ? TRUE : FALSE);
1181 return (laser.overloaded ? TRUE : FALSE);
1184 if (element == EL_FUEL_FULL)
1186 laser.stops_inside_element = TRUE;
1191 if (element == EL_BOMB || element == EL_MINE)
1193 PlaySoundStereo(SND_KINK, ST(ELX));
1195 if (element == EL_MINE)
1196 laser.overloaded = TRUE;
1199 if (element == EL_KETTLE ||
1200 element == EL_CELL ||
1201 element == EL_KEY ||
1202 element == EL_LIGHTBALL ||
1203 element == EL_PACMAN ||
1206 if (!IS_PACMAN(element))
1209 if (element == EL_PACMAN)
1212 if (element == EL_KETTLE || element == EL_CELL)
1214 if (game_mm.kettles_still_needed > 0)
1215 game_mm.kettles_still_needed--;
1219 if (game_mm.kettles_still_needed == 0)
1222 static int xy[4][2] =
1230 PlaySoundStereo(SND_KLING, ST(ELX));
1232 for (y = 0; y < lev_fieldy; y++)
1234 for (x = 0; x < lev_fieldx; x++)
1236 /* initiate opening animation of exit door */
1237 if (Feld[x][y] == EL_EXIT_CLOSED)
1238 Feld[x][y] = EL_EXIT_OPENING;
1240 /* remove field that blocks receiver */
1241 if (IS_RECEIVER(Feld[x][y]))
1243 int phase = Feld[x][y] - EL_RECEIVER_START;
1244 int blocking_x, blocking_y;
1246 blocking_x = x + xy[phase][0];
1247 blocking_y = y + xy[phase][1];
1249 if (IN_LEV_FIELD(blocking_x, blocking_y))
1251 Feld[blocking_x][blocking_y] = EL_EMPTY;
1253 DrawField_MM(blocking_x, blocking_y);
1259 DrawLaser(0, DL_LASER_ENABLED);
1262 else if (element == EL_KEY)
1266 else if (element == EL_LIGHTBALL)
1270 else if (IS_PACMAN(element))
1272 DeletePacMan(ELX, ELY);
1279 if (element == EL_LIGHTBULB_OFF || element == EL_LIGHTBULB_ON)
1281 PlaySoundStereo(SND_KINK, ST(ELX));
1283 DrawLaser(0, DL_LASER_ENABLED);
1285 if (Feld[ELX][ELY] == EL_LIGHTBULB_OFF)
1287 Feld[ELX][ELY] = EL_LIGHTBULB_ON;
1288 game_mm.lights_still_needed--;
1292 Feld[ELX][ELY] = EL_LIGHTBULB_OFF;
1293 game_mm.lights_still_needed++;
1296 DrawField_MM(ELX, ELY);
1297 DrawLaser(0, DL_LASER_ENABLED);
1302 laser.stops_inside_element = TRUE;
1308 printf("HitElement (4): element == %d\n", element);
1311 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1312 laser.num_beamers < MAX_NUM_BEAMERS &&
1313 laser.beamer[BEAMER_NR(element)][1].num)
1315 int beamer_angle = get_element_angle(element);
1316 int beamer_nr = BEAMER_NR(element);
1320 printf("HitElement (BEAMER): element == %d\n", element);
1323 laser.num_damages--;
1325 if (IS_FIBRE_OPTIC(element) ||
1326 laser.current_angle == get_opposite_angle(beamer_angle))
1330 LX = ELX * TILEX + 14;
1331 LY = ELY * TILEY + 14;
1333 AddLaserEdge(LX, LY);
1334 AddDamagedField(ELX, ELY);
1336 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1339 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1341 pos = (ELX == laser.beamer[beamer_nr][0].x &&
1342 ELY == laser.beamer[beamer_nr][0].y ? 1 : 0);
1343 ELX = laser.beamer[beamer_nr][pos].x;
1344 ELY = laser.beamer[beamer_nr][pos].y;
1345 LX = ELX * TILEX + 14;
1346 LY = ELY * TILEY + 14;
1348 if (IS_BEAMER(element))
1350 laser.current_angle = get_element_angle(Feld[ELX][ELY]);
1351 XS = 2 * Step[laser.current_angle].x;
1352 YS = 2 * Step[laser.current_angle].y;
1355 laser.beamer_edge[laser.num_beamers] = laser.num_edges;
1357 AddLaserEdge(LX, LY);
1358 AddDamagedField(ELX, ELY);
1360 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1363 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1365 if (laser.current_angle == (laser.current_angle >> 1) << 1)
1370 LX += step_size * XS;
1371 LY += step_size * YS;
1373 laser.num_beamers++;
1382 boolean HitOnlyAnEdge(int element, int hit_mask)
1384 /* check if the laser hit only the edge of an element and, if so, go on */
1387 printf("LX, LY, hit_mask == %d, %d, %d\n", LX, LY, hit_mask);
1390 if ((hit_mask == HIT_MASK_TOPLEFT ||
1391 hit_mask == HIT_MASK_TOPRIGHT ||
1392 hit_mask == HIT_MASK_BOTTOMLEFT ||
1393 hit_mask == HIT_MASK_BOTTOMRIGHT) &&
1394 laser.current_angle % 4) /* angle is not 90° */
1398 if (hit_mask == HIT_MASK_TOPLEFT)
1403 else if (hit_mask == HIT_MASK_TOPRIGHT)
1408 else if (hit_mask == HIT_MASK_BOTTOMLEFT)
1413 else /* (hit_mask == HIT_MASK_BOTTOMRIGHT) */
1419 AddDamagedField((LX + 2 * dx) / TILEX, (LY + 2 * dy) / TILEY);
1425 printf("[HitOnlyAnEdge() == TRUE]\n");
1432 printf("[HitOnlyAnEdge() == FALSE]\n");
1438 boolean HitPolarizer(int element, int hit_mask)
1440 if (HitOnlyAnEdge(element, hit_mask))
1443 if (IS_DF_GRID(element))
1445 int grid_angle = get_element_angle(element);
1448 printf("HitPolarizer: angle: grid == %d, laser == %d\n",
1449 grid_angle, laser.current_angle);
1452 AddLaserEdge(LX, LY);
1453 AddDamagedField(ELX, ELY);
1456 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1458 if (laser.current_angle == grid_angle ||
1459 laser.current_angle == get_opposite_angle(grid_angle))
1461 /* skip the whole element before continuing the scan */
1467 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1469 if (LX/TILEX > ELX || LY/TILEY > ELY)
1471 /* skipping scan positions to the right and down skips one scan
1472 position too much, because this is only the top left scan position
1473 of totally four scan positions (plus one to the right, one to the
1474 bottom and one to the bottom right) */
1480 AddLaserEdge(LX, LY);
1486 printf("HitPolarizer: LX, LY == %d, %d [%d, %d] [%d, %d]\n",
1488 LX / TILEX, LY / TILEY,
1489 LX % TILEX, LY % TILEY);
1494 else if (IS_GRID_STEEL_FIXED(element) || IS_GRID_STEEL_AUTO(element))
1496 return HitReflectingWalls(element, hit_mask);
1500 return HitAbsorbingWalls(element, hit_mask);
1503 else if (IS_GRID_STEEL(element))
1505 return HitReflectingWalls(element, hit_mask);
1507 else /* IS_GRID_WOOD */
1509 return HitAbsorbingWalls(element, hit_mask);
1515 boolean HitBlock(int element, int hit_mask)
1517 boolean check = FALSE;
1519 if ((element == EL_GATE_STONE || element == EL_GATE_WOOD) &&
1520 game_mm.num_keys == 0)
1523 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1526 int ex = ELX * TILEX + 14;
1527 int ey = ELY * TILEY + 14;
1531 for (i = 1; i < 32; i++)
1536 if ((x == ex || x == ex + 1) && (y == ey || y == ey + 1))
1541 if (check && (element == EL_BLOCK_WOOD || element == EL_GATE_WOOD))
1542 return HitAbsorbingWalls(element, hit_mask);
1546 AddLaserEdge(LX - XS, LY - YS);
1547 AddDamagedField(ELX, ELY);
1550 Box[ELX][ELY] = laser.num_edges;
1552 return HitReflectingWalls(element, hit_mask);
1555 if (element == EL_GATE_STONE || element == EL_GATE_WOOD)
1557 int xs = XS / 2, ys = YS / 2;
1558 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1559 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1561 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1562 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1564 laser.overloaded = (element == EL_GATE_STONE);
1569 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1570 (hit_mask == HIT_MASK_TOP ||
1571 hit_mask == HIT_MASK_LEFT ||
1572 hit_mask == HIT_MASK_RIGHT ||
1573 hit_mask == HIT_MASK_BOTTOM))
1574 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1575 hit_mask == HIT_MASK_BOTTOM),
1576 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1577 hit_mask == HIT_MASK_RIGHT));
1578 AddLaserEdge(LX, LY);
1584 if (element == EL_GATE_STONE && Box[ELX][ELY])
1586 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
1598 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1600 int xs = XS / 2, ys = YS / 2;
1601 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1602 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1604 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1605 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1607 laser.overloaded = (element == EL_BLOCK_STONE);
1612 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1613 (hit_mask == HIT_MASK_TOP ||
1614 hit_mask == HIT_MASK_LEFT ||
1615 hit_mask == HIT_MASK_RIGHT ||
1616 hit_mask == HIT_MASK_BOTTOM))
1617 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1618 hit_mask == HIT_MASK_BOTTOM),
1619 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1620 hit_mask == HIT_MASK_RIGHT));
1621 AddDamagedField(ELX, ELY);
1623 LX = ELX * TILEX + 14;
1624 LY = ELY * TILEY + 14;
1626 AddLaserEdge(LX, LY);
1628 laser.stops_inside_element = TRUE;
1636 boolean HitLaserSource(int element, int hit_mask)
1638 if (HitOnlyAnEdge(element, hit_mask))
1641 PlaySoundStereo(SND_AUTSCH, ST(ELX));
1642 laser.overloaded = TRUE;
1647 boolean HitLaserDestination(int element, int hit_mask)
1649 if (HitOnlyAnEdge(element, hit_mask))
1652 if (element != EL_EXIT_OPEN &&
1653 !(IS_RECEIVER(element) &&
1654 game_mm.kettles_still_needed == 0 &&
1655 laser.current_angle == get_opposite_angle(get_element_angle(element))))
1657 PlaySoundStereo(SND_HOLZ, ST(ELX));
1661 if (IS_RECEIVER(element) ||
1662 (IS_22_5_ANGLE(laser.current_angle) &&
1663 (ELX != (LX + 6 * XS) / TILEX ||
1664 ELY != (LY + 6 * YS) / TILEY ||
1673 LX = ELX * TILEX + 14;
1674 LY = ELY * TILEY + 14;
1676 laser.stops_inside_element = TRUE;
1679 AddLaserEdge(LX, LY);
1680 AddDamagedField(ELX, ELY);
1682 if (game_mm.lights_still_needed == 0)
1683 game_mm.level_solved = TRUE;
1688 boolean HitReflectingWalls(int element, int hit_mask)
1690 /* check if laser hits side of a wall with an angle that is not 90° */
1691 if (!IS_90_ANGLE(laser.current_angle) && (hit_mask == HIT_MASK_TOP ||
1692 hit_mask == HIT_MASK_LEFT ||
1693 hit_mask == HIT_MASK_RIGHT ||
1694 hit_mask == HIT_MASK_BOTTOM))
1696 PlaySoundStereo(SND_HUI, ST(ELX));
1701 if (!IS_DF_GRID(element))
1702 AddLaserEdge(LX, LY);
1704 /* check if laser hits wall with an angle of 45° */
1705 if (!IS_22_5_ANGLE(laser.current_angle))
1707 if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1710 laser.current_angle = get_mirrored_angle(laser.current_angle,
1713 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1716 laser.current_angle = get_mirrored_angle(laser.current_angle,
1720 AddLaserEdge(LX, LY);
1722 XS = 2 * Step[laser.current_angle].x;
1723 YS = 2 * Step[laser.current_angle].y;
1727 else if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1729 laser.current_angle = get_mirrored_angle(laser.current_angle,
1734 if (!IS_DF_GRID(element))
1735 AddLaserEdge(LX, LY);
1740 if (!IS_DF_GRID(element))
1741 AddLaserEdge(LX, LY + YS / 2);
1744 if (!IS_DF_GRID(element))
1745 AddLaserEdge(LX, LY);
1748 YS = 2 * Step[laser.current_angle].y;
1752 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1754 laser.current_angle = get_mirrored_angle(laser.current_angle,
1759 if (!IS_DF_GRID(element))
1760 AddLaserEdge(LX, LY);
1765 if (!IS_DF_GRID(element))
1766 AddLaserEdge(LX + XS / 2, LY);
1769 if (!IS_DF_GRID(element))
1770 AddLaserEdge(LX, LY);
1773 XS = 2 * Step[laser.current_angle].x;
1779 /* reflection at the edge of reflecting DF style wall */
1780 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1782 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1783 hit_mask == HIT_MASK_TOPRIGHT) ||
1784 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1785 hit_mask == HIT_MASK_TOPLEFT) ||
1786 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1787 hit_mask == HIT_MASK_BOTTOMLEFT) ||
1788 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1789 hit_mask == HIT_MASK_BOTTOMRIGHT))
1792 (hit_mask == HIT_MASK_TOPRIGHT || hit_mask == HIT_MASK_BOTTOMLEFT ?
1793 ANG_MIRROR_135 : ANG_MIRROR_45);
1795 PlaySoundStereo(SND_HUI, ST(ELX));
1797 AddDamagedField(ELX, ELY);
1798 AddLaserEdge(LX, LY);
1800 laser.current_angle = get_mirrored_angle(laser.current_angle,
1808 AddLaserEdge(LX, LY);
1814 /* reflection inside an edge of reflecting DF style wall */
1815 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1817 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1818 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT)) ||
1819 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1820 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMRIGHT)) ||
1821 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1822 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT)) ||
1823 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1824 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPLEFT)))
1827 (hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT) ||
1828 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT) ?
1829 ANG_MIRROR_135 : ANG_MIRROR_45);
1831 PlaySoundStereo(SND_HUI, ST(ELX));
1834 AddDamagedField(ELX, ELY);
1837 AddLaserEdge(LX - XS, LY - YS);
1838 AddLaserEdge(LX - XS + (ABS(XS) == 4 ? XS/2 : 0),
1839 LY - YS + (ABS(YS) == 4 ? YS/2 : 0));
1841 laser.current_angle = get_mirrored_angle(laser.current_angle,
1849 AddLaserEdge(LX, LY);
1855 /* check if laser hits DF style wall with an angle of 90° */
1856 if (IS_DF_WALL(element) && IS_90_ANGLE(laser.current_angle))
1858 if ((IS_HORIZ_ANGLE(laser.current_angle) &&
1859 (!(hit_mask & HIT_MASK_TOP) || !(hit_mask & HIT_MASK_BOTTOM))) ||
1860 (IS_VERT_ANGLE(laser.current_angle) &&
1861 (!(hit_mask & HIT_MASK_LEFT) || !(hit_mask & HIT_MASK_RIGHT))))
1863 static int last_LX = 0, last_LY = 0, last_hit_mask = 0;
1865 /* laser at last step touched nothing or the same side of the wall */
1866 if (LX != last_LX || LY != last_LY || hit_mask == last_hit_mask)
1868 AddDamagedField(ELX, ELY);
1875 last_hit_mask = hit_mask;
1882 if (!HitOnlyAnEdge(element, hit_mask))
1884 laser.overloaded = TRUE;
1892 boolean HitAbsorbingWalls(int element, int hit_mask)
1894 if (HitOnlyAnEdge(element, hit_mask))
1898 (hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT))
1900 AddLaserEdge(LX - XS, LY - YS);
1907 (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM))
1909 AddLaserEdge(LX - XS, LY - YS);
1915 if (IS_WALL_WOOD(element) ||
1916 IS_DF_WALL_WOOD(element) ||
1917 IS_GRID_WOOD(element) ||
1918 IS_GRID_WOOD_FIXED(element) ||
1919 IS_GRID_WOOD_AUTO(element) ||
1920 element == EL_FUSE_ON ||
1921 element == EL_BLOCK_WOOD ||
1922 element == EL_GATE_WOOD)
1924 PlaySoundStereo(SND_HOLZ, ST(ELX));
1929 if (IS_WALL_ICE(element))
1933 mask = (LX + XS) / MINI_TILEX - ELX * 2 + 1; /* Quadrant (horizontal) */
1934 mask <<= (((LY + YS) / MINI_TILEY - ELY * 2) > 0) * 2; /* || (vertical) */
1936 /* check if laser hits wall with an angle of 90° */
1937 if (IS_90_ANGLE(laser.current_angle))
1938 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1940 if (mask == 1 || mask == 2 || mask == 4 || mask == 8)
1944 for (i = 0; i < 4; i++)
1946 if (mask == (1 << i) && (XS > 0) == (i % 2) && (YS > 0) == (i / 2))
1947 mask = 15 - (8 >> i);
1948 else if (ABS(XS) == 4 &&
1950 (XS > 0) == (i % 2) &&
1951 (YS < 0) == (i / 2))
1952 mask = 3 + (i / 2) * 9;
1953 else if (ABS(YS) == 4 &&
1955 (XS < 0) == (i % 2) &&
1956 (YS > 0) == (i / 2))
1957 mask = 5 + (i % 2) * 5;
1961 laser.wall_mask = mask;
1963 else if (IS_WALL_AMOEBA(element))
1965 int elx = (LX - 2 * XS) / TILEX;
1966 int ely = (LY - 2 * YS) / TILEY;
1967 int element2 = Feld[elx][ely];
1970 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element2))
1972 laser.dest_element = EL_EMPTY;
1980 mask = (LX - 2 * XS) / 16 - ELX * 2 + 1;
1981 mask <<= ((LY - 2 * YS) / 16 - ELY * 2) * 2;
1983 if (IS_90_ANGLE(laser.current_angle))
1984 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1986 laser.dest_element = element2 | EL_WALL_AMOEBA;
1988 laser.wall_mask = mask;
1994 void OpenExit(int x, int y)
1998 if (!MovDelay[x][y]) /* next animation frame */
1999 MovDelay[x][y] = 4 * delay;
2001 if (MovDelay[x][y]) /* wait some time before next frame */
2006 phase = MovDelay[x][y] / delay;
2008 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2009 DrawGraphicAnimation_MM(x, y, IMG_MM_EXIT_OPENING, 3 - phase);
2011 if (!MovDelay[x][y])
2013 Feld[x][y] = EL_EXIT_OPEN;
2019 void OpenSurpriseBall(int x, int y)
2023 if (!MovDelay[x][y]) /* next animation frame */
2024 MovDelay[x][y] = 50 * delay;
2026 if (MovDelay[x][y]) /* wait some time before next frame */
2030 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2033 int graphic = el2gfx(Store[x][y]);
2035 int dx = RND(26), dy = RND(26);
2037 getGraphicSource(graphic, 0, &bitmap, &gx, &gy);
2039 BlitBitmap(bitmap, drawto, gx + dx, gy + dy, 6, 6,
2040 SX + x * TILEX + dx, SY + y * TILEY + dy);
2042 MarkTileDirty(x, y);
2045 if (!MovDelay[x][y])
2047 Feld[x][y] = Store[x][y];
2056 void MeltIce(int x, int y)
2061 if (!MovDelay[x][y]) /* next animation frame */
2062 MovDelay[x][y] = frames * delay;
2064 if (MovDelay[x][y]) /* wait some time before next frame */
2067 int wall_mask = Store2[x][y];
2068 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_ICE;
2071 phase = frames - MovDelay[x][y] / delay - 1;
2073 if (!MovDelay[x][y])
2077 Feld[x][y] = real_element & (wall_mask ^ 0xFF);
2078 Store[x][y] = Store2[x][y] = 0;
2080 DrawWalls_MM(x, y, Feld[x][y]);
2082 if (Feld[x][y] == EL_WALL_ICE)
2083 Feld[x][y] = EL_EMPTY;
2085 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
2086 if (laser.damage[i].is_mirror)
2090 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
2092 DrawLaser(0, DL_LASER_DISABLED);
2096 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2098 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2100 laser.redraw = TRUE;
2105 void GrowAmoeba(int x, int y)
2110 if (!MovDelay[x][y]) /* next animation frame */
2111 MovDelay[x][y] = frames * delay;
2113 if (MovDelay[x][y]) /* wait some time before next frame */
2116 int wall_mask = Store2[x][y];
2117 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_AMOEBA;
2120 phase = MovDelay[x][y] / delay;
2122 if (!MovDelay[x][y])
2124 Feld[x][y] = real_element;
2125 Store[x][y] = Store2[x][y] = 0;
2127 DrawWalls_MM(x, y, Feld[x][y]);
2128 DrawLaser(0, DL_LASER_ENABLED);
2130 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2132 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2137 static void Explode_MM(int x, int y, int phase, int mode)
2139 int num_phase = 9, delay = 2;
2140 int last_phase = num_phase * delay;
2141 int half_phase = (num_phase / 2) * delay;
2143 laser.redraw = TRUE;
2145 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2147 int center_element = Feld[x][y];
2149 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2151 /* put moving element to center field (and let it explode there) */
2152 center_element = MovingOrBlocked2Element_MM(x, y);
2153 RemoveMovingField_MM(x, y);
2155 Feld[x][y] = center_element;
2158 if (center_element == EL_BOMB || IS_MCDUFFIN(center_element))
2159 Store[x][y] = center_element;
2161 Store[x][y] = EL_EMPTY;
2163 Store2[x][y] = mode;
2164 Feld[x][y] = EL_EXPLODING_OPAQUE;
2165 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2171 Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
2173 if (phase == half_phase)
2175 Feld[x][y] = EL_EXPLODING_TRANSP;
2177 if (x == ELX && y == ELY)
2181 if (phase == last_phase)
2183 if (Store[x][y] == EL_BOMB)
2185 laser.num_damages--;
2186 DrawLaser(0, DL_LASER_DISABLED);
2187 laser.num_edges = 0;
2189 Bang_MM(laser.start_edge.x, laser.start_edge.y);
2190 Store[x][y] = EL_EMPTY;
2192 else if (IS_MCDUFFIN(Store[x][y]))
2194 game_mm.game_over = TRUE;
2195 game_mm.game_over_cause = GAME_OVER_BOMB;
2196 Store[x][y] = EL_EMPTY;
2199 Feld[x][y] = Store[x][y];
2200 Store[x][y] = Store2[x][y] = 0;
2201 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2203 InitField(x, y, FALSE);
2206 else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2208 int graphic = IMG_MM_DEFAULT_EXPLODING;
2209 int graphic_phase = (phase / delay - 1);
2213 if (Store2[x][y] == EX_KETTLE)
2215 if (graphic_phase < 3)
2217 graphic = IMG_MM_KETTLE_EXPLODING;
2219 else if (graphic_phase < 5)
2225 graphic = IMG_EMPTY;
2229 else if (Store2[x][y] == EX_SHORT)
2231 if (graphic_phase < 4)
2237 graphic = IMG_EMPTY;
2242 getGraphicSource(graphic, graphic_phase, &bitmap, &src_x, &src_y);
2244 BlitBitmap(bitmap, drawto_field, src_x, src_y, TILEX, TILEY,
2245 FX + x * TILEX, FY + y * TILEY);
2247 MarkTileDirty(x, y);
2251 static void Bang_MM(int x, int y)
2253 int element = Feld[x][y];
2254 int mode = EX_NORMAL;
2257 DrawLaser(0, DL_LASER_ENABLED);
2276 if (IS_PACMAN(element))
2277 PlaySoundStereo(SND_QUIEK, ST(x));
2278 else if (element == EL_BOMB || IS_MCDUFFIN(element))
2279 PlaySoundStereo(SND_ROAAAR, ST(x));
2280 else if (element == EL_KEY)
2281 PlaySoundStereo(SND_KLING, ST(x));
2283 PlaySoundStereo((mode == EX_SHORT ? SND_WHOOSH : SND_KABUMM), ST(x));
2285 Explode_MM(x, y, EX_PHASE_START, mode);
2288 void TurnRound(int x, int y)
2300 { 0, 0 }, { 0, 0 }, { 0, 0 },
2305 int left, right, back;
2309 { MV_DOWN, MV_UP, MV_RIGHT },
2310 { MV_UP, MV_DOWN, MV_LEFT },
2312 { MV_LEFT, MV_RIGHT, MV_DOWN },
2313 { 0,0,0 }, { 0,0,0 }, { 0,0,0 },
2314 { MV_RIGHT, MV_LEFT, MV_UP }
2317 int element = Feld[x][y];
2318 int old_move_dir = MovDir[x][y];
2319 int right_dir = turn[old_move_dir].right;
2320 int back_dir = turn[old_move_dir].back;
2321 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
2322 int right_x = x + right_dx, right_y = y + right_dy;
2324 if (element == EL_PACMAN)
2326 boolean can_turn_right = FALSE;
2328 if (IN_LEV_FIELD(right_x, right_y) &&
2329 IS_EATABLE4PACMAN(Feld[right_x][right_y]))
2330 can_turn_right = TRUE;
2333 MovDir[x][y] = right_dir;
2335 MovDir[x][y] = back_dir;
2341 static void StartMoving_MM(int x, int y)
2343 int element = Feld[x][y];
2348 if (CAN_MOVE(element))
2352 if (MovDelay[x][y]) /* wait some time before next movement */
2360 /* now make next step */
2362 Moving2Blocked_MM(x, y, &newx, &newy); /* get next screen position */
2364 if (element == EL_PACMAN &&
2365 IN_LEV_FIELD(newx, newy) && IS_EATABLE4PACMAN(Feld[newx][newy]) &&
2366 !ObjHit(newx, newy, HIT_POS_CENTER))
2368 Store[newx][newy] = Feld[newx][newy];
2369 Feld[newx][newy] = EL_EMPTY;
2371 DrawField_MM(newx, newy);
2373 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy) ||
2374 ObjHit(newx, newy, HIT_POS_CENTER))
2376 /* object was running against a wall */
2383 InitMovingField_MM(x, y, MovDir[x][y]);
2387 ContinueMoving_MM(x, y);
2390 static void ContinueMoving_MM(int x, int y)
2392 int element = Feld[x][y];
2393 int direction = MovDir[x][y];
2394 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2395 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2396 int horiz_move = (dx!=0);
2397 int newx = x + dx, newy = y + dy;
2398 int step = (horiz_move ? dx : dy) * TILEX / 8;
2400 MovPos[x][y] += step;
2402 if (ABS(MovPos[x][y]) >= TILEX) /* object reached its destination */
2404 Feld[x][y] = EL_EMPTY;
2405 Feld[newx][newy] = element;
2407 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2408 MovDelay[newx][newy] = 0;
2410 if (!CAN_MOVE(element))
2411 MovDir[newx][newy] = 0;
2414 DrawField_MM(newx, newy);
2416 Stop[newx][newy] = TRUE;
2418 if (element == EL_PACMAN)
2420 if (Store[newx][newy] == EL_BOMB)
2421 Bang_MM(newx, newy);
2423 if (IS_WALL_AMOEBA(Store[newx][newy]) &&
2424 (LX + 2 * XS) / TILEX == newx &&
2425 (LY + 2 * YS) / TILEY == newy)
2432 else /* still moving on */
2437 laser.redraw = TRUE;
2440 void ClickElement(int mx, int my, int button)
2442 static unsigned int click_delay = 0;
2443 static int click_delay_value = CLICK_DELAY_SHORT;
2444 static boolean new_button = TRUE;
2446 int x = (mx - SX) / TILEX, y = (my - SY) / TILEY;
2448 /* do not rotate objects hit by the laser after the game was solved */
2449 if (game_mm.level_solved && Hit[x][y])
2452 if (button == MB_RELEASED)
2455 click_delay_value = CLICK_DELAY_SHORT;
2457 /* release eventually hold auto-rotating mirror */
2458 RotateMirror(x, y, MB_RELEASED);
2463 if (!DelayReached(&click_delay, click_delay_value) && !new_button)
2466 if (button == MB_MIDDLEBUTTON) /* middle button has no function */
2469 if (!IN_PIX_FIELD(mx - SX, my - SY))
2472 if (Feld[x][y] == EL_EMPTY)
2475 element = Feld[x][y];
2477 if (IS_MIRROR(element) ||
2478 IS_BEAMER(element) ||
2479 IS_POLAR(element) ||
2480 IS_POLAR_CROSS(element) ||
2481 IS_DF_MIRROR(element) ||
2482 IS_DF_MIRROR_AUTO(element))
2484 RotateMirror(x, y, button);
2486 else if (IS_MCDUFFIN(element))
2488 if (!laser.fuse_off)
2490 DrawLaser(0, DL_LASER_DISABLED);
2497 element = get_rotated_element(element, BUTTON_ROTATION(button));
2498 laser.start_angle = get_element_angle(element);
2502 Feld[x][y] = element;
2509 if (!laser.fuse_off)
2512 else if (element == EL_FUSE_ON && laser.fuse_off)
2514 if (x != laser.fuse_x || y != laser.fuse_y)
2517 laser.fuse_off = FALSE;
2518 laser.fuse_x = laser.fuse_y = -1;
2520 DrawGraphic_MM(x, y, IMG_MM_FUSE_ACTIVE);
2523 else if (element == EL_FUSE_ON && !laser.fuse_off && new_button)
2525 laser.fuse_off = TRUE;
2528 laser.overloaded = FALSE;
2530 DrawLaser(0, DL_LASER_DISABLED);
2531 DrawGraphic_MM(x, y, IMG_MM_FUSE);
2533 else if (element == EL_LIGHTBALL)
2537 DrawLaser(0, DL_LASER_ENABLED);
2540 click_delay_value = (new_button ? CLICK_DELAY_LONG : CLICK_DELAY_SHORT);
2544 void RotateMirror(int x, int y, int button)
2546 static int hold_x = -1, hold_y = -1;
2548 if (button == MB_RELEASED)
2550 /* release eventually hold auto-rotating mirror */
2557 if (IS_MIRROR(Feld[x][y]) ||
2558 IS_POLAR_CROSS(Feld[x][y]) ||
2559 IS_POLAR(Feld[x][y]) ||
2560 IS_BEAMER(Feld[x][y]) ||
2561 IS_DF_MIRROR(Feld[x][y]) ||
2562 IS_GRID_STEEL_AUTO(Feld[x][y]) ||
2563 IS_GRID_WOOD_AUTO(Feld[x][y]))
2565 Feld[x][y] = get_rotated_element(Feld[x][y], BUTTON_ROTATION(button));
2567 else if (IS_DF_MIRROR_AUTO(Feld[x][y]))
2569 if (button == MB_LEFTBUTTON)
2571 /* left mouse button only for manual adjustment, no auto-rotating;
2572 freeze mirror for until mouse button released */
2576 else if (button == MB_RIGHTBUTTON && (hold_x != x || hold_y != y))
2578 Feld[x][y] = get_rotated_element(Feld[x][y], ROTATE_RIGHT);
2582 if (IS_GRID_STEEL_AUTO(Feld[x][y]) || IS_GRID_WOOD_AUTO(Feld[x][y]))
2584 int edge = Hit[x][y];
2590 DrawLaser(edge - 1, DL_LASER_DISABLED);
2594 else if (ObjHit(x, y, HIT_POS_CENTER))
2596 int edge = Hit[x][y];
2600 Error(ERR_WARN, "RotateMirror: inconsistent field Hit[][]!\n");
2604 DrawLaser(edge - 1, DL_LASER_DISABLED);
2611 if (ObjHit(x, y, HIT_POS_EDGE | HIT_POS_BETWEEN))
2616 if ((IS_BEAMER(Feld[x][y]) ||
2617 IS_POLAR(Feld[x][y]) ||
2618 IS_POLAR_CROSS(Feld[x][y])) && x == ELX && y == ELY)
2622 if (IS_BEAMER(Feld[x][y]))
2625 printf("TEST (%d, %d) [%d] [%d]\n",
2627 laser.beamer_edge, laser.beamer[1].num);
2637 DrawLaser(0, DL_LASER_ENABLED);
2641 void AutoRotateMirrors()
2643 static unsigned int rotate_delay = 0;
2646 if (!DelayReached(&rotate_delay, AUTO_ROTATE_DELAY))
2649 for (x = 0; x < lev_fieldx; x++)
2651 for (y = 0; y < lev_fieldy; y++)
2653 int element = Feld[x][y];
2655 /* do not rotate objects hit by the laser after the game was solved */
2656 if (game_mm.level_solved && Hit[x][y])
2659 if (IS_DF_MIRROR_AUTO(element) ||
2660 IS_GRID_WOOD_AUTO(element) ||
2661 IS_GRID_STEEL_AUTO(element) ||
2662 element == EL_REFRACTOR)
2663 RotateMirror(x, y, MB_RIGHTBUTTON);
2668 boolean ObjHit(int obx, int oby, int bits)
2675 if (bits & HIT_POS_CENTER)
2677 if (ReadPixel(drawto, SX + obx + 15, SY + oby + 15) == pen_ray)
2681 if (bits & HIT_POS_EDGE)
2683 for (i = 0; i < 4; i++)
2684 if (ReadPixel(drawto,
2685 SX + obx + 31 * (i % 2),
2686 SY + oby + 31 * (i / 2)) == pen_ray)
2690 if (bits & HIT_POS_BETWEEN)
2692 for (i = 0; i < 4; i++)
2693 if (ReadPixel(drawto,
2694 SX + 4 + obx + 22 * (i % 2),
2695 SY + 4 + oby + 22 * (i / 2)) == pen_ray)
2702 void DeletePacMan(int px, int py)
2708 if (game_mm.num_pacman <= 1)
2710 game_mm.num_pacman = 0;
2714 for (i = 0; i < game_mm.num_pacman; i++)
2715 if (game_mm.pacman[i].x == px && game_mm.pacman[i].y == py)
2718 game_mm.num_pacman--;
2720 for (j = i; j < game_mm.num_pacman; j++)
2722 game_mm.pacman[j].x = game_mm.pacman[j + 1].x;
2723 game_mm.pacman[j].y = game_mm.pacman[j + 1].y;
2724 game_mm.pacman[j].dir = game_mm.pacman[j + 1].dir;
2728 void ColorCycling(void)
2730 static int CC, Cc = 0;
2732 static int color, old = 0xF00, new = 0x010, mult = 1;
2733 static unsigned short red, green, blue;
2735 if (color_status == STATIC_COLORS)
2740 if (CC < Cc || CC > Cc + 50)
2744 color = old + new * mult;
2750 if (ABS(mult) == 16)
2760 red = 0x0e00 * ((color & 0xF00) >> 8);
2761 green = 0x0e00 * ((color & 0x0F0) >> 4);
2762 blue = 0x0e00 * ((color & 0x00F));
2763 SetRGB(pen_magicolor[0], red, green, blue);
2765 red = 0x1111 * ((color & 0xF00) >> 8);
2766 green = 0x1111 * ((color & 0x0F0) >> 4);
2767 blue = 0x1111 * ((color & 0x00F));
2768 SetRGB(pen_magicolor[1], red, green, blue);
2772 static void GameActions_MM_Ext(byte action[MAX_PLAYERS], boolean warp_mode)
2774 static unsigned int action_delay = 0;
2775 static unsigned int pacman_delay = 0;
2776 static unsigned int energy_delay = 0;
2777 static unsigned int overload_delay = 0;
2783 WaitUntilDelayReached(&action_delay, GameFrameDelay);
2785 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2788 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2790 element = Feld[x][y];
2792 if (!IS_MOVING(x, y) && CAN_MOVE(element))
2793 StartMoving_MM(x, y);
2794 else if (IS_MOVING(x, y))
2795 ContinueMoving_MM(x, y);
2796 else if (IS_EXPLODING(element))
2797 Explode_MM(x, y, Frame[x][y], EX_NORMAL);
2798 else if (element == EL_EXIT_OPENING)
2800 else if (element == EL_GRAY_BALL_OPENING)
2801 OpenSurpriseBall(x, y);
2802 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_ICE)
2804 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_AMOEBA)
2808 AutoRotateMirrors();
2811 /* !!! CHANGE THIS: REDRAW ONLY WHEN NEEDED !!! */
2813 /* redraw after Explode_MM() ... */
2815 DrawLaser(0, DL_LASER_ENABLED);
2816 laser.redraw = FALSE;
2821 if (game_mm.num_pacman && DelayReached(&pacman_delay, 250))
2825 if (laser.num_damages > MAX_LASER_LEN && !laser.fuse_off)
2827 DrawLaser(0, DL_LASER_DISABLED);
2832 if (DelayReached(&energy_delay, 4000))
2834 game_mm.energy_left--;
2835 if (game_mm.energy_left >= 0)
2838 BlitBitmap(pix[PIX_DOOR], drawto,
2839 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
2840 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
2841 DX_ENERGY, DY_ENERGY);
2843 redraw_mask |= REDRAW_DOOR_1;
2845 else if (setup.time_limit)
2849 for (i = 15; i >= 0; i--)
2852 SetRGB(pen_ray, 0x0000, 0x0000, i * color_scale);
2854 pen_ray = GetPixelFromRGB(window,
2855 native_mm_level.laser_red * 0x11 * i,
2856 native_mm_level.laser_green * 0x11 * i,
2857 native_mm_level.laser_blue * 0x11 * i);
2859 DrawLaser(0, DL_LASER_ENABLED);
2864 StopSound(SND_WARNTON);
2867 DrawLaser(0, DL_LASER_DISABLED);
2868 game_mm.game_over = TRUE;
2869 game_mm.game_over_cause = GAME_OVER_NO_ENERGY;
2872 if (Request("Out of magic energy ! Play it again ?",
2873 REQ_ASK | REQ_STAY_CLOSED))
2879 game_status = MAINMENU;
2888 element = laser.dest_element;
2891 if (element != Feld[ELX][ELY])
2893 printf("element == %d, Feld[ELX][ELY] == %d\n",
2894 element, Feld[ELX][ELY]);
2898 if (!laser.overloaded && laser.overload_value == 0 &&
2899 element != EL_BOMB &&
2900 element != EL_MINE &&
2901 element != EL_BALL_GRAY &&
2902 element != EL_BLOCK_STONE &&
2903 element != EL_BLOCK_WOOD &&
2904 element != EL_FUSE_ON &&
2905 element != EL_FUEL_FULL &&
2906 !IS_WALL_ICE(element) &&
2907 !IS_WALL_AMOEBA(element))
2910 if (((laser.overloaded && laser.overload_value < MAX_LASER_OVERLOAD) ||
2911 (!laser.overloaded && laser.overload_value > 0)) &&
2912 DelayReached(&overload_delay, 60 + !laser.overloaded * 120))
2914 if (laser.overloaded)
2915 laser.overload_value++;
2917 laser.overload_value--;
2919 if (game_mm.cheat_no_overload)
2921 laser.overloaded = FALSE;
2922 laser.overload_value = 0;
2925 if (laser.overload_value < MAX_LASER_OVERLOAD - 8)
2927 int color_up = 0xFF * laser.overload_value / MAX_LASER_OVERLOAD;
2928 int color_down = 0xFF - color_up;
2931 SetRGB(pen_ray, (laser.overload_value / 6) * color_scale, 0x0000,
2932 (15 - (laser.overload_value / 6)) * color_scale);
2935 GetPixelFromRGB(window,
2936 (native_mm_level.laser_red ? 0xFF : color_up),
2937 (native_mm_level.laser_green ? color_down : 0x00),
2938 (native_mm_level.laser_blue ? color_down : 0x00));
2940 DrawLaser(0, DL_LASER_ENABLED);
2944 if (laser.overloaded)
2946 if (setup.sound_loops)
2947 PlaySoundExt(SND_WARNTON, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
2948 SND_CTRL_PLAY_LOOP);
2950 PlaySoundStereo(SND_WARNTON, SOUND_MAX_RIGHT);
2953 if (!laser.overloaded)
2954 StopSound(SND_WARNTON);
2956 if (laser.overloaded)
2959 BlitBitmap(pix[PIX_DOOR], drawto,
2960 DOOR_GFX_PAGEX4 + XX_OVERLOAD,
2961 DOOR_GFX_PAGEY1 + YY_OVERLOAD + OVERLOAD_YSIZE
2962 - laser.overload_value,
2963 OVERLOAD_XSIZE, laser.overload_value,
2964 DX_OVERLOAD, DY_OVERLOAD + OVERLOAD_YSIZE
2965 - laser.overload_value);
2967 redraw_mask |= REDRAW_DOOR_1;
2972 BlitBitmap(pix[PIX_DOOR], drawto,
2973 DOOR_GFX_PAGEX5 + XX_OVERLOAD, DOOR_GFX_PAGEY1 + YY_OVERLOAD,
2974 OVERLOAD_XSIZE, OVERLOAD_YSIZE - laser.overload_value,
2975 DX_OVERLOAD, DY_OVERLOAD);
2977 redraw_mask |= REDRAW_DOOR_1;
2980 if (laser.overload_value == MAX_LASER_OVERLOAD)
2984 for (i = 15; i >= 0; i--)
2987 SetRGB(pen_ray, i * color_scale, 0x0000, 0x0000);
2990 pen_ray = GetPixelFromRGB(window, 0x11 * i, 0x00, 0x00);
2992 DrawLaser(0, DL_LASER_ENABLED);
2997 DrawLaser(0, DL_LASER_DISABLED);
2999 game_mm.game_over = TRUE;
3000 game_mm.game_over_cause = GAME_OVER_OVERLOADED;
3003 if (Request("Magic spell hit Mc Duffin ! Play it again ?",
3004 REQ_ASK | REQ_STAY_CLOSED))
3010 game_status = MAINMENU;
3024 if (element == EL_BOMB && CT > 1500)
3026 if (game_mm.cheat_no_explosion)
3030 laser.num_damages--;
3031 DrawLaser(0, DL_LASER_DISABLED);
3032 laser.num_edges = 0;
3037 laser.dest_element = EL_EXPLODING_OPAQUE;
3041 laser.num_damages--;
3042 DrawLaser(0, DL_LASER_DISABLED);
3044 laser.num_edges = 0;
3045 Bang_MM(laser.start_edge.x, laser.start_edge.y);
3047 if (Request("Bomb killed Mc Duffin ! Play it again ?",
3048 REQ_ASK | REQ_STAY_CLOSED))
3054 game_status = MAINMENU;
3062 if (element == EL_FUSE_ON && CT > 500)
3064 laser.fuse_off = TRUE;
3068 DrawLaser(0, DL_LASER_DISABLED);
3069 DrawGraphic_MM(ELX, ELY, IMG_MM_FUSE);
3072 if (element == EL_BALL_GRAY && CT > 1500)
3074 static int new_elements[] =
3077 EL_MIRROR_FIXED_START,
3079 EL_POLAR_CROSS_START,
3085 int num_new_elements = sizeof(new_elements) / sizeof(int);
3086 int new_element = new_elements[RND(num_new_elements)];
3088 Store[ELX][ELY] = new_element + RND(get_num_elements(new_element));
3089 Feld[ELX][ELY] = EL_GRAY_BALL_OPENING;
3091 /* !!! CHECK AGAIN: Laser on Polarizer !!! */
3102 element = EL_MIRROR_START + RND(16);
3108 element = (rnd == 0 ? EL_KETTLE : rnd == 1 ? EL_BOMB : EL_PRISM);
3115 element = (rnd == 0 ? EL_FUSE_ON :
3116 rnd >= 1 && rnd <= 4 ? EL_PACMAN_RIGHT + rnd - 1 :
3117 rnd >= 5 && rnd <= 20 ? EL_POLAR_START + rnd - 5 :
3118 rnd >= 21 && rnd <= 24 ? EL_POLAR_CROSS_START + rnd - 21 :
3119 EL_MIRROR_FIXED_START + rnd - 25);
3124 graphic = el2gfx(element);
3126 for (i = 0; i < 50; i++)
3132 BlitBitmap(pix[PIX_BACK], drawto,
3133 SX + (graphic % GFX_PER_LINE) * TILEX + x,
3134 SY + (graphic / GFX_PER_LINE) * TILEY + y, 6, 6,
3135 SX + ELX * TILEX + x,
3136 SY + ELY * TILEY + y);
3138 MarkTileDirty(ELX, ELY);
3141 DrawLaser(0, DL_LASER_ENABLED);
3146 Feld[ELX][ELY] = element;
3147 DrawField_MM(ELX, ELY);
3150 printf("NEW ELEMENT: (%d, %d)\n", ELX, ELY);
3153 /* above stuff: GRAY BALL -> PRISM !!! */
3155 LX = ELX * TILEX + 14;
3156 LY = ELY * TILEY + 14;
3157 if (laser.current_angle == (laser.current_angle >> 1) << 1)
3164 laser.num_edges -= 2;
3165 laser.num_damages--;
3169 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
3170 if (laser.damage[i].is_mirror)
3174 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3176 DrawLaser(0, DL_LASER_DISABLED);
3178 DrawLaser(0, DL_LASER_DISABLED);
3184 printf("TEST ELEMENT: %d\n", Feld[0][0]);
3191 if (IS_WALL_ICE(element) && CT > 1000)
3193 PlaySoundStereo(SND_SLURP, ST(ELX));
3196 Feld[ELX][ELY] = Feld[ELX][ELY] - EL_WALL_ICE + EL_WALL_CHANGING;
3197 Store[ELX][ELY] = EL_WALL_ICE;
3198 Store2[ELX][ELY] = laser.wall_mask;
3200 laser.dest_element = Feld[ELX][ELY];
3205 for (i = 0; i < 5; i++)
3211 Feld[ELX][ELY] &= (laser.wall_mask ^ 0xFF);
3215 DrawWallsAnimation_MM(ELX, ELY, Feld[ELX][ELY], phase, laser.wall_mask);
3220 if (Feld[ELX][ELY] == EL_WALL_ICE)
3221 Feld[ELX][ELY] = EL_EMPTY;
3225 LX = laser.edge[laser.num_edges].x - (SX + 2);
3226 LY = laser.edge[laser.num_edges].y - (SY + 2);
3229 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
3230 if (laser.damage[i].is_mirror)
3234 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3236 DrawLaser(0, DL_LASER_DISABLED);
3243 if (IS_WALL_AMOEBA(element) && CT > 1200)
3245 int k1, k2, k3, dx, dy, de, dm;
3246 int element2 = Feld[ELX][ELY];
3248 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element))
3251 for (i = laser.num_damages - 1; i >= 0; i--)
3252 if (laser.damage[i].is_mirror)
3255 r = laser.num_edges;
3256 d = laser.num_damages;
3263 DrawLaser(laser.damage[k1].edge - 1, DL_LASER_DISABLED);
3266 DrawLaser(0, DL_LASER_ENABLED);
3269 x = laser.damage[k1].x;
3270 y = laser.damage[k1].y;
3275 for (i = 0; i < 4; i++)
3277 if (laser.wall_mask & (1 << i))
3279 if (ReadPixel(drawto,
3280 SX + ELX * TILEX + 14 + (i % 2) * 2,
3281 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3283 if (ReadPixel(drawto,
3284 SX + ELX * TILEX + 31 * (i % 2),
3285 SY + ELY * TILEY + 14 + (i / 2) * 2) == pen_ray)
3292 for (i = 0; i < 4; i++)
3294 if (laser.wall_mask & (1 << i))
3296 if (ReadPixel(drawto,
3297 SX + ELX * TILEX + 31 * (i % 2),
3298 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3305 if (laser.num_beamers > 0 ||
3306 k1 < 1 || k2 < 4 || k3 < 4 ||
3307 ReadPixel(drawto, SX + ELX * TILEX + 14, SY + ELY * TILEY + 14)
3310 laser.num_edges = r;
3311 laser.num_damages = d;
3313 DrawLaser(0, DL_LASER_DISABLED);
3316 Feld[ELX][ELY] = element | laser.wall_mask;
3320 de = Feld[ELX][ELY];
3321 dm = laser.wall_mask;
3325 int x = ELX, y = ELY;
3326 int wall_mask = laser.wall_mask;
3329 DrawLaser(0, DL_LASER_ENABLED);
3331 PlaySoundStereo(SND_AMOEBE, ST(dx));
3333 Feld[x][y] = Feld[x][y] - EL_WALL_AMOEBA + EL_WALL_CHANGING;
3334 Store[x][y] = EL_WALL_AMOEBA;
3335 Store2[x][y] = wall_mask;
3341 DrawWallsAnimation_MM(dx, dy, de, 4, dm);
3343 DrawLaser(0, DL_LASER_ENABLED);
3345 PlaySoundStereo(SND_AMOEBE, ST(dx));
3347 for (i = 4; i >= 0; i--)
3349 DrawWallsAnimation_MM(dx, dy, de, i, dm);
3355 DrawLaser(0, DL_LASER_ENABLED);
3360 if ((element == EL_BLOCK_WOOD || element == EL_BLOCK_STONE) &&
3361 laser.stops_inside_element && CT > 1500)
3366 if (ABS(XS) > ABS(YS))
3373 for (i = 0; i < 4; i++)
3380 x = ELX + Step[k * 4].x;
3381 y = ELY + Step[k * 4].y;
3383 if (!IN_LEV_FIELD(x, y) || Feld[x][y] != EL_EMPTY)
3386 if (ObjHit(x, y, HIT_POS_CENTER | HIT_POS_EDGE | HIT_POS_BETWEEN))
3394 laser.overloaded = (element == EL_BLOCK_STONE);
3399 PlaySoundStereo(SND_BONG, ST(ELX));
3402 Feld[x][y] = element;
3404 DrawGraphic_MM(ELX, ELY, IMG_EMPTY);
3407 if (element == EL_BLOCK_STONE && Box[ELX][ELY])
3409 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
3410 DrawLaser(laser.num_edges - 1, DL_LASER_ENABLED);
3418 if (element == EL_FUEL_FULL && CT > 200)
3420 for (i = game_mm.energy_left; i <= MAX_LASER_ENERGY; i+=2)
3423 BlitBitmap(pix[PIX_DOOR], drawto,
3424 DOOR_GFX_PAGEX4 + XX_ENERGY,
3425 DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
3426 ENERGY_XSIZE, i, DX_ENERGY,
3427 DY_ENERGY + ENERGY_YSIZE - i);
3430 redraw_mask |= REDRAW_DOOR_1;
3436 game_mm.energy_left = MAX_LASER_ENERGY;
3437 Feld[ELX][ELY] = EL_FUEL_EMPTY;
3438 DrawField_MM(ELX, ELY);
3440 DrawLaser(0, DL_LASER_ENABLED);
3448 void GameActions_MM(byte action[MAX_PLAYERS], boolean warp_mode)
3451 ClickElement(0, 0, MB_NOT_PRESSED);
3453 GameActions_MM_Ext(action, warp_mode);
3459 int mx, my, ox, oy, nx, ny;
3463 if (++p >= game_mm.num_pacman)
3466 game_mm.pacman[p].dir--;
3468 for (l = 1; l < 5; l++)
3470 game_mm.pacman[p].dir++;
3472 if (game_mm.pacman[p].dir > 4)
3473 game_mm.pacman[p].dir = 1;
3475 if (game_mm.pacman[p].dir % 2)
3478 my = game_mm.pacman[p].dir - 2;
3483 mx = 3 - game_mm.pacman[p].dir;
3486 ox = game_mm.pacman[p].x;
3487 oy = game_mm.pacman[p].y;
3490 element = Feld[nx][ny];
3492 if (nx < 0 || nx > 15 || ny < 0 || ny > 11)
3495 if (!IS_EATABLE4PACMAN(element))
3498 if (ObjHit(nx, ny, HIT_POS_CENTER))
3501 Feld[ox][oy] = EL_EMPTY;
3503 EL_PACMAN_RIGHT - 1 +
3504 (game_mm.pacman[p].dir - 1 +
3505 (game_mm.pacman[p].dir % 2) * 2);
3507 game_mm.pacman[p].x = nx;
3508 game_mm.pacman[p].y = ny;
3510 DrawGraphic_MM(ox, oy, IMG_EMPTY);
3512 if (element != EL_EMPTY)
3514 int graphic = el2gfx(Feld[nx][ny]);
3519 getGraphicSource(graphic, 0, &bitmap, &src_x, &src_y);
3522 ox = SX + ox * TILEX;
3523 oy = SY + oy * TILEY;
3525 for (i = 1; i < 33; i += 2)
3526 BlitBitmap(bitmap, window,
3527 src_x, src_y, TILEX, TILEY,
3528 ox + i * mx, oy + i * my);
3529 Ct = Ct + Counter() - CT;
3532 DrawField_MM(nx, ny);
3535 if (!laser.fuse_off)
3537 DrawLaser(0, DL_LASER_ENABLED);
3539 if (ObjHit(nx, ny, HIT_POS_BETWEEN))
3541 AddDamagedField(nx, ny);
3543 laser.damage[laser.num_damages - 1].edge = 0;
3547 if (element == EL_BOMB)
3548 DeletePacMan(nx, ny);
3550 if (IS_WALL_AMOEBA(element) &&
3551 (LX + 2 * XS) / TILEX == nx &&
3552 (LY + 2 * YS) / TILEY == ny)
3565 boolean raise_level = FALSE;
3568 if (local_player->MovPos)
3571 local_player->LevelSolved = FALSE;
3574 if (game_mm.energy_left)
3576 if (setup.sound_loops)
3577 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3578 SND_CTRL_PLAY_LOOP);
3580 while (game_mm.energy_left > 0)
3582 if (!setup.sound_loops)
3583 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3586 if (game_mm.energy_left > 0 && !(game_mm.energy_left % 10))
3587 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3592 game_mm.energy_left--;
3593 if (game_mm.energy_left >= 0)
3596 BlitBitmap(pix[PIX_DOOR], drawto,
3597 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
3598 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
3599 DX_ENERGY, DY_ENERGY);
3601 redraw_mask |= REDRAW_DOOR_1;
3608 if (setup.sound_loops)
3609 StopSound(SND_SIRR);
3611 else if (native_mm_level.time == 0) /* level without time limit */
3613 if (setup.sound_loops)
3614 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3615 SND_CTRL_PLAY_LOOP);
3617 while (TimePlayed < 999)
3619 if (!setup.sound_loops)
3620 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3621 if (TimePlayed < 999 && !(TimePlayed % 10))
3622 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3623 if (TimePlayed < 900 && !(TimePlayed % 10))
3629 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
3636 if (setup.sound_loops)
3637 StopSound(SND_SIRR);
3644 CloseDoor(DOOR_CLOSE_1);
3646 Request("Level solved !", REQ_CONFIRM);
3648 if (level_nr == leveldir_current->handicap_level)
3650 leveldir_current->handicap_level++;
3651 SaveLevelSetup_SeriesInfo();
3654 if (level_editor_test_game)
3655 game_mm.score = -1; /* no highscore when playing from editor */
3656 else if (level_nr < leveldir_current->last_level)
3657 raise_level = TRUE; /* advance to next level */
3659 if ((hi_pos = NewHiScore_MM()) >= 0)
3661 game_status = HALLOFFAME;
3663 // DrawHallOfFame(hi_pos);
3670 game_status = MAINMENU;
3686 // LoadScore(level_nr);
3688 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
3689 game_mm.score < highscore[MAX_SCORE_ENTRIES - 1].Score)
3692 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3694 if (game_mm.score > highscore[k].Score)
3696 /* player has made it to the hall of fame */
3698 if (k < MAX_SCORE_ENTRIES - 1)
3700 int m = MAX_SCORE_ENTRIES - 1;
3703 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3704 if (!strcmp(setup.player_name, highscore[l].Name))
3706 if (m == k) /* player's new highscore overwrites his old one */
3710 for (l = m; l>k; l--)
3712 strcpy(highscore[l].Name, highscore[l - 1].Name);
3713 highscore[l].Score = highscore[l - 1].Score;
3720 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3721 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3722 highscore[k].Score = game_mm.score;
3729 else if (!strncmp(setup.player_name, highscore[k].Name,
3730 MAX_PLAYER_NAME_LEN))
3731 break; /* player already there with a higher score */
3736 // if (position >= 0)
3737 // SaveScore(level_nr);
3742 static void InitMovingField_MM(int x, int y, int direction)
3744 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3745 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3747 MovDir[x][y] = direction;
3748 MovDir[newx][newy] = direction;
3750 if (Feld[newx][newy] == EL_EMPTY)
3751 Feld[newx][newy] = EL_BLOCKED;
3754 static void Moving2Blocked_MM(int x, int y, int *goes_to_x, int *goes_to_y)
3756 int direction = MovDir[x][y];
3757 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3758 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3764 static void Blocked2Moving_MM(int x, int y,
3765 int *comes_from_x, int *comes_from_y)
3767 int oldx = x, oldy = y;
3768 int direction = MovDir[x][y];
3770 if (direction == MV_LEFT)
3772 else if (direction == MV_RIGHT)
3774 else if (direction == MV_UP)
3776 else if (direction == MV_DOWN)
3779 *comes_from_x = oldx;
3780 *comes_from_y = oldy;
3783 static int MovingOrBlocked2Element_MM(int x, int y)
3785 int element = Feld[x][y];
3787 if (element == EL_BLOCKED)
3791 Blocked2Moving_MM(x, y, &oldx, &oldy);
3793 return Feld[oldx][oldy];
3800 static void RemoveField(int x, int y)
3802 Feld[x][y] = EL_EMPTY;
3809 static void RemoveMovingField_MM(int x, int y)
3811 int oldx = x, oldy = y, newx = x, newy = y;
3813 if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
3816 if (IS_MOVING(x, y))
3818 Moving2Blocked_MM(x, y, &newx, &newy);
3819 if (Feld[newx][newy] != EL_BLOCKED)
3822 else if (Feld[x][y] == EL_BLOCKED)
3824 Blocked2Moving_MM(x, y, &oldx, &oldy);
3825 if (!IS_MOVING(oldx, oldy))
3829 Feld[oldx][oldy] = EL_EMPTY;
3830 Feld[newx][newy] = EL_EMPTY;
3831 MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
3832 MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
3834 DrawLevelField_MM(oldx, oldy);
3835 DrawLevelField_MM(newx, newy);
3838 void PlaySoundLevel(int x, int y, int sound_nr)
3840 int sx = SCREENX(x), sy = SCREENY(y);
3842 int silence_distance = 8;
3844 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
3845 (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
3848 if (!IN_LEV_FIELD(x, y) ||
3849 sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
3850 sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
3853 volume = SOUND_MAX_VOLUME;
3856 stereo = (sx - SCR_FIELDX/2) * 12;
3858 stereo = SOUND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
3859 if (stereo > SOUND_MAX_RIGHT)
3860 stereo = SOUND_MAX_RIGHT;
3861 if (stereo < SOUND_MAX_LEFT)
3862 stereo = SOUND_MAX_LEFT;
3865 if (!IN_SCR_FIELD(sx, sy))
3867 int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
3868 int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
3870 volume -= volume * (dx > dy ? dx : dy) / silence_distance;
3873 PlaySoundExt(sound_nr, volume, stereo, SND_CTRL_PLAY_SOUND);
3876 static void RaiseScore_MM(int value)
3878 game_mm.score += value;
3881 DrawText(DX_SCORE, DY_SCORE, int2str(game_mm.score, 4),
3886 void RaiseScoreElement_MM(int element)
3891 RaiseScore_MM(native_mm_level.score[SC_PACMAN]);
3895 RaiseScore_MM(native_mm_level.score[SC_KEY]);