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))
206 int phase = element - EL_PACMAN_RIGHT;
208 game_mm.pacman[game_mm.num_pacman].x = x;
209 game_mm.pacman[game_mm.num_pacman].y = y;
210 game_mm.pacman[game_mm.num_pacman].dir = phase + ((phase + 1) % 2) * 2;
211 game_mm.num_pacman++;
216 else if (IS_MCDUFFIN(element) || IS_LASER(element))
218 laser.start_edge.x = x;
219 laser.start_edge.y = y;
220 laser.start_angle = get_element_angle(element);
227 static void InitCycleElements()
231 if (game_mm.num_cycle == 0) /* no elements to cycle */
236 for(j=0; j<game_mm.num_cycle; j++)
238 int x = game_mm.cycle[j].x;
239 int y = game_mm.cycle[j].y;
240 int step = SIGN(game_mm.cycle[j].steps);
241 int last_element = Feld[x][y];
242 int next_element = get_rotated_element(last_element, step);
244 if (!game_mm.cycle[j].steps)
247 Feld[x][y] = next_element;
250 game_mm.cycle[j].steps -= step;
257 if (setup.quick_doors)
261 Delay(AUTO_ROTATE_DELAY);
265 static void InitLaser()
267 int start_element = Feld[laser.start_edge.x][laser.start_edge.y];
268 int step = (IS_LASER(start_element) ? 4 : 0);
270 LX = laser.start_edge.x * TILEX;
271 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
274 LX += (laser.start_angle == ANG_RAY_RIGHT ? 28 + step : 0 - step);
276 LY = laser.start_edge.y * TILEY;
277 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
278 LY += (laser.start_angle == ANG_RAY_DOWN ? 28 + step : 0 - step);
282 XS = 2 * Step[laser.start_angle].x;
283 YS = 2 * Step[laser.start_angle].y;
285 laser.current_angle = laser.start_angle;
287 laser.num_damages = 0;
289 laser.num_beamers = 0;
290 laser.beamer_edge[0] = 0;
292 AddLaserEdge(LX, LY); /* set laser starting edge */
294 pen_ray = GetPixelFromRGB(window,
295 native_mm_level.laser_red * 0xFF,
296 native_mm_level.laser_green * 0xFF,
297 native_mm_level.laser_blue * 0xFF);
300 void InitGameEngine_MM()
304 /* set global editor control values */
305 editor.draw_walls_masked = FALSE;
307 /* set global game control values */
308 game_mm.num_cycle = 0;
309 game_mm.num_pacman = 0;
312 game_mm.energy_left = native_mm_level.time;
313 game_mm.kettles_still_needed =
314 (native_mm_level.auto_count_kettles ? 0 : native_mm_level.kettles_needed);
315 game_mm.lights_still_needed = 0;
316 game_mm.num_keys = 0;
318 game_mm.level_solved = FALSE;
319 game_mm.game_over = FALSE;
320 game_mm.game_over_cause = 0;
322 /* set global laser control values (must be set before "InitLaser()") */
323 laser.start_edge.x = 0;
324 laser.start_edge.y = 0;
325 laser.start_angle = 0;
327 for (i=0; i<MAX_NUM_BEAMERS; i++)
328 laser.beamer[i][0].num = laser.beamer[i][1].num = 0;
330 laser.overloaded = FALSE;
331 laser.overload_value = 0;
332 laser.fuse_off = FALSE;
333 laser.fuse_x = laser.fuse_y = -1;
335 laser.dest_element = EL_EMPTY;
340 for (x=0; x<lev_fieldx; x++)
342 for (y=0; y<lev_fieldy; y++)
344 Feld[x][y] = Ur[x][y];
345 Hit[x][y] = Box[x][y] = 0;
347 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
348 Store[x][y] = Store2[x][y] = 0;
352 InitField(x, y, TRUE);
356 CloseDoor(DOOR_CLOSE_1);
363 /* copy default game door content to main double buffer */
364 BlitBitmap(pix[PIX_DOOR], drawto,
365 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
369 DrawText(DX_LEVEL, DY_LEVEL,
370 int2str(level_nr, 2), FONT_TEXT_2);
371 DrawText(DX_KETTLES, DY_KETTLES,
372 int2str(game_mm.kettles_still_needed, 3), FONT_TEXT_2);
373 DrawText(DX_SCORE, DY_SCORE,
374 int2str(game_mm.score, 4), FONT_TEXT_2);
379 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
380 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
381 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
386 /* copy actual game door content to door double buffer for OpenDoor() */
387 BlitBitmap(drawto, pix[PIX_DB_DOOR],
388 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
391 OpenDoor(DOOR_OPEN_ALL);
393 if (setup.sound_loops)
394 PlaySoundExt(SND_FUEL, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT, SND_CTRL_PLAY_LOOP);
396 #if 0 // !!! TEMPORARILY DISABLED !!!
397 for(i=0; i<=game_mm.energy_left; i+=2)
399 if (!setup.sound_loops)
400 PlaySoundStereo(SND_FUEL, SOUND_MAX_RIGHT);
403 BlitBitmap(pix[PIX_DOOR], drawto,
404 DOOR_GFX_PAGEX4 + XX_ENERGY,
405 DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
407 DX_ENERGY, DY_ENERGY + ENERGY_YSIZE - i);
410 redraw_mask |= REDRAW_DOOR_1;
416 if (setup.quick_doors)
423 if (setup.sound_loops)
428 if (setup.sound_music && num_bg_loops)
429 PlayMusic(level_nr % num_bg_loops);
435 void AddLaserEdge(int lx, int ly)
437 if (lx < -2 || ly < -2 || lx >= SXSIZE + 2 || ly >= SYSIZE + 2)
439 Error(ERR_WARN, "AddLaserEdge: out of bounds: %d, %d", lx, ly);
443 laser.edge[laser.num_edges].x = SX + 2 + lx;
444 laser.edge[laser.num_edges].y = SY + 2 + ly;
450 void AddDamagedField(int ex, int ey)
452 laser.damage[laser.num_damages].is_mirror = FALSE;
453 laser.damage[laser.num_damages].angle = laser.current_angle;
454 laser.damage[laser.num_damages].edge = laser.num_edges;
455 laser.damage[laser.num_damages].x = ex;
456 laser.damage[laser.num_damages].y = ey;
466 int last_x = laser.edge[laser.num_edges - 1].x - SX - 2;
467 int last_y = laser.edge[laser.num_edges - 1].y - SY - 2;
469 return ((x - last_x) * XS < 0 || (y - last_y) * YS < 0);
475 static int getMaskFromElement(int element)
477 if (IS_GRID(element))
478 return GFX_MASK_GRID_00 + get_element_phase(element);
479 else if (IS_MCDUFFIN(element))
480 return GFX_MASK_MCDUFFIN_00 + get_element_phase(element);
481 else if (IS_RECTANGLE(element) || IS_DF_GRID(element))
482 return GFX_MASK_RECTANGLE;
484 return GFX_MASK_CIRCLE;
492 printf("ScanPixel: start scanning at (%d, %d) [%d, %d] [%d, %d]\n",
493 LX, LY, LX / TILEX, LY / TILEY, LX % TILEX, LY % TILEY);
496 /* follow laser beam until it hits something (at least the screen border) */
497 while (hit_mask == HIT_MASK_NO_HIT)
503 if (SX + LX < REAL_SX || SX + LX >= REAL_SX + FULL_SXSIZE ||
504 SY + LY < REAL_SY || SY + LY >= REAL_SY + FULL_SYSIZE)
506 printf("ScanPixel: touched screen border!\n");
517 px = SX + LX + (i % 2) * 2;
518 py = SY + LY + (i / 2) * 2;
519 lx = (px - SX + TILEX) / TILEX - 1; /* ...+TILEX...-1 to get correct */
520 ly = (py - SY + TILEY) / TILEY - 1; /* negative values! */
522 if (IN_LEV_FIELD(lx, ly))
524 int element = Feld[lx][ly];
526 if (element == EL_EMPTY || element == EL_EXPLODING_TRANSP)
528 else if (IS_WALL(element) || IS_WALL_CHANGING(element))
531 ((py - SY - ly * TILEY) / MINI_TILEX) * 2 +
532 (px - SX - lx * TILEX) / MINI_TILEY;
534 pixel = ((element & (1 << pos)) ? 1 : 0);
538 int graphic_mask = getMaskFromElement(element);
540 int dx = px - lx * TILEX;
541 int dy = py - ly * TILEY;
543 mask_x = (graphic_mask % GFX_PER_LINE) * TILEX + dx;
544 mask_y = (graphic_mask / GFX_PER_LINE) * TILEY + dy;
547 // !!! temporary fix to compile -- change to game graphics !!!
548 pixel = (ReadPixel(drawto, mask_x, mask_y) ? 1 : 0);
550 pixel = (ReadPixel(pix[PIX_BACK], mask_x, mask_y) ? 1 : 0);
556 if (px < REAL_SX || px >= REAL_SX + FULL_SXSIZE ||
557 py < REAL_SY || py >= REAL_SY + FULL_SYSIZE)
563 if ((Sign[laser.current_angle] & (1 << i)) && pixel)
564 hit_mask |= (1 << i);
567 if (hit_mask == HIT_MASK_NO_HIT)
569 /* hit nothing -- go on with another step */
581 int end = 0, rf = laser.num_edges;
583 unsigned short color_scale = 0xFFFF / 15;
589 laser.overloaded = FALSE;
590 laser.stops_inside_element = FALSE;
593 if (laser.overload_value < MAX_LASER_OVERLOAD - 8)
595 (laser.overload_value / 6) * color_scale, 0x0000,
596 (15 - (laser.overload_value / 6)) * color_scale);
599 DrawLaser(0, DL_LASER_ENABLED);
602 printf("Start scanning with LX == %d, LY == %d, XS == %d, YS == %d\n",
610 if (laser.num_edges > MAX_LASER_LEN || laser.num_damages > MAX_LASER_LEN)
613 laser.overloaded = TRUE;
617 hit_mask = ScanPixel();
620 printf("Hit something at LX == %d, LY == %d, XS == %d, YS == %d\n",
624 /* hit something -- check out what it was */
625 ELX = (LX + XS) / TILEX;
626 ELY = (LY + YS) / TILEY;
629 printf("hit_mask (1) == '%x' (%d, %d) (%d, %d)\n",
630 hit_mask, LX, LY, ELX, ELY);
633 if (!IN_LEV_FIELD(ELX, ELY) || !IN_PIX_FIELD(LX, LY))
636 laser.dest_element = element;
641 if (hit_mask == (HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT))
643 /* we have hit the top-right and bottom-left element --
644 choose the bottom-left one */
645 /* !!! THIS CAN BE DONE MORE INTELLIGENTLY, FOR EXAMPLE, IF ONE
646 ELEMENT WAS STEEL AND THE OTHER ONE WAS ICE => ALWAYS CHOOSE
647 THE ICE AND MELT IT AWAY INSTEAD OF OVERLOADING LASER !!! */
648 ELX = (LX - 2) / TILEX;
649 ELY = (LY + 2) / TILEY;
652 if (hit_mask == (HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT))
654 /* we have hit the top-left and bottom-right element --
655 choose the top-left one */
656 /* !!! SEE ABOVE !!! */
657 ELX = (LX - 2) / TILEX;
658 ELY = (LY - 2) / TILEY;
663 printf("hit_mask (2) == '%x' (%d, %d) (%d, %d)\n",
664 hit_mask, LX, LY, ELX, ELY);
667 element = Feld[ELX][ELY];
668 laser.dest_element = element;
671 printf("Hit element %d at (%d, %d) [%d, %d] [%d, %d] [%d]\n",
674 LX % TILEX, LY % TILEY,
679 if (!IN_LEV_FIELD(ELX, ELY))
680 printf("WARNING! (1) %d, %d (%d)\n", ELX, ELY, element);
688 if (element == EL_EMPTY)
690 if (!HitOnlyAnEdge(element, hit_mask))
693 else if (element == EL_FUSE_ON)
695 if (HitPolarizer(element, hit_mask))
698 else if (IS_GRID(element) || IS_DF_GRID(element))
700 if (HitPolarizer(element, hit_mask))
703 else if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD ||
704 element == EL_GATE_STONE || element == EL_GATE_WOOD)
706 if (HitBlock(element, hit_mask))
712 else if (IS_MCDUFFIN(element))
714 if (HitLaserSource(element, hit_mask))
717 else if ((element >= EL_EXIT_CLOSED && element <= EL_EXIT_OPEN) ||
718 IS_RECEIVER(element))
720 if (HitLaserDestination(element, hit_mask))
723 else if (IS_WALL(element))
725 if (IS_WALL_STEEL(element) || IS_DF_WALL_STEEL(element))
727 if (HitReflectingWalls(element, hit_mask))
732 if (HitAbsorbingWalls(element, hit_mask))
738 if (HitElement(element, hit_mask))
743 DrawLaser(rf - 1, DL_LASER_ENABLED);
744 rf = laser.num_edges;
748 element = Feld[ELX][ELY];
749 laser.dest_element = element;
756 if (laser.dest_element != Feld[ELX][ELY])
758 printf("ALARM: laser.dest_element == %d, Feld[ELX][ELY] == %d\n",
759 laser.dest_element, Feld[ELX][ELY]);
764 if (!end && !laser.stops_inside_element && !StepBehind())
767 printf("ScanLaser: Go one step back\n");
772 AddLaserEdge(LX, LY);
776 DrawLaser(rf - 1, DL_LASER_ENABLED);
782 if (!IN_LEV_FIELD(ELX, ELY))
783 printf("WARNING! (2) %d, %d\n", ELX, ELY);
788 printf("(%d, %d) == %d [(%d, %d) == %d]\n",
789 testx, testy, laser.dest_element,
790 ELX, ELY, (IN_SCR_FIELD(ELX,ELY) ? Feld[ELX][ELY] : -1));
795 void DrawLaserExt(int start_edge, int num_edges, int mode)
801 printf("DrawLaserExt: start_edge, num_edges, mode == %d, %d, %d\n",
802 start_edge, num_edges, mode);
807 Error(ERR_WARN, "DrawLaserExt: start_edge < 0");
813 Error(ERR_WARN, "DrawLaserExt: num_edges < 0");
818 if (mode == DL_LASER_DISABLED)
820 printf("DrawLaser: Delete laser from edge %d\n", start_edge);
824 /* now draw the laser to the backbuffer and (if enabled) to the screen */
825 DrawLines(drawto, &laser.edge[start_edge], num_edges,
826 (mode == DL_LASER_ENABLED ? pen_ray : pen_bg));
828 redraw_mask |= REDRAW_FIELD;
830 if (mode == DL_LASER_ENABLED)
833 /* after the laser was deleted, the "damaged" graphics must be restored */
834 if (laser.num_damages)
836 int damage_start = 0;
839 /* determine the starting edge, from which graphics need to be restored */
842 for(i=0; i<laser.num_damages; i++)
844 if (laser.damage[i].edge == start_edge + 1)
852 /* restore graphics from this starting edge to the end of damage list */
853 for(i=damage_start; i<laser.num_damages; i++)
855 int lx = laser.damage[i].x;
856 int ly = laser.damage[i].y;
857 int element = Feld[lx][ly];
859 if (Hit[lx][ly] == laser.damage[i].edge)
860 if (!((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
863 if (Box[lx][ly] == laser.damage[i].edge)
866 if (IS_DRAWABLE(element))
867 DrawField_MM(lx, ly);
870 elx = laser.damage[damage_start].x;
871 ely = laser.damage[damage_start].y;
872 element = Feld[elx][ely];
876 if (IS_BEAMER(element))
880 for (i=0; i<laser.num_beamers; i++)
881 printf("-> %d\n", laser.beamer_edge[i]);
882 printf("DrawLaserExt: IS_BEAMER: [%d]: Hit[%d][%d] == %d [%d]\n",
883 mode, elx, ely, Hit[elx][ely], start_edge);
884 printf("DrawLaserExt: IS_BEAMER: %d / %d\n",
885 get_element_angle(element), laser.damage[damage_start].angle);
889 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
890 laser.num_beamers > 0 &&
891 start_edge == laser.beamer_edge[laser.num_beamers - 1])
893 /* element is outgoing beamer */
894 laser.num_damages = damage_start + 1;
895 if (IS_BEAMER(element))
896 laser.current_angle = get_element_angle(element);
900 /* element is incoming beamer or other element */
901 laser.num_damages = damage_start;
902 laser.current_angle = laser.damage[laser.num_damages].angle;
907 /* no damages but McDuffin himself (who needs to be redrawn anyway) */
909 elx = laser.start_edge.x;
910 ely = laser.start_edge.y;
911 element = Feld[elx][ely];
914 laser.num_edges = start_edge + 1;
916 laser.current_angle = laser.start_angle;
917 LX = laser.edge[start_edge].x - (SX + 2);
918 LY = laser.edge[start_edge].y - (SY + 2);
919 XS = 2 * Step[laser.current_angle].x;
920 YS = 2 * Step[laser.current_angle].y;
923 printf("DrawLaser: Set (LX, LY) to (%d, %d) [%d]\n",
929 if (IS_BEAMER(element) ||
930 IS_FIBRE_OPTIC(element) ||
931 IS_PACMAN(element) ||
933 IS_POLAR_CROSS(element) ||
934 element == EL_FUSE_ON)
939 printf("element == %d\n", element);
942 if (IS_22_5_ANGLE(laser.current_angle)) /* neither 90° nor 45° angle */
943 step_size = ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) ? 4 : 3);
947 if (IS_POLAR(element) || IS_POLAR_CROSS(element) ||
948 ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
949 (laser.num_beamers == 0 ||
950 start_edge != laser.beamer_edge[laser.num_beamers - 1])))
952 /* element is incoming beamer or other element */
953 step_size = -step_size;
958 if (IS_BEAMER(element))
960 printf("start_edge == %d, laser.beamer_edge == %d\n",
961 start_edge, laser.beamer_edge);
965 LX += step_size * XS;
966 LY += step_size * YS;
968 else if (element != EL_EMPTY)
977 printf("DrawLaser: Finally: (LX, LY) to (%d, %d) [%d]\n",
982 void DrawLaser(int start_edge, int mode)
984 if (laser.num_edges - start_edge < 0)
986 Error(ERR_WARN, "DrawLaser: laser.num_edges - start_edge < 0");
990 /* check if laser is interrupted by beamer element */
991 if (laser.num_beamers > 0 &&
992 start_edge < laser.beamer_edge[laser.num_beamers - 1])
994 if (mode == DL_LASER_ENABLED)
997 int tmp_start_edge = start_edge;
999 /* draw laser segments forward from the start to the last beamer */
1000 for (i=0; i<laser.num_beamers; i++)
1002 int tmp_num_edges = laser.beamer_edge[i] - tmp_start_edge;
1004 if (tmp_num_edges <= 0)
1008 printf("DrawLaser: DL_LASER_ENABLED: i==%d: %d, %d\n",
1009 i, laser.beamer_edge[i], tmp_start_edge);
1012 DrawLaserExt(tmp_start_edge, tmp_num_edges, DL_LASER_ENABLED);
1013 tmp_start_edge = laser.beamer_edge[i];
1016 /* draw last segment from last beamer to the end */
1017 DrawLaserExt(tmp_start_edge, laser.num_edges - tmp_start_edge,
1023 int last_num_edges = laser.num_edges;
1024 int num_beamers = laser.num_beamers;
1026 /* delete laser segments backward from the end to the first beamer */
1027 for (i=num_beamers-1; i>=0; i--)
1029 int tmp_num_edges = last_num_edges - laser.beamer_edge[i];
1031 if (laser.beamer_edge[i] - start_edge <= 0)
1034 DrawLaserExt(laser.beamer_edge[i], tmp_num_edges, DL_LASER_DISABLED);
1035 last_num_edges = laser.beamer_edge[i];
1036 laser.num_beamers--;
1040 if (last_num_edges - start_edge <= 0)
1041 printf("DrawLaser: DL_LASER_DISABLED: %d, %d\n",
1042 last_num_edges, start_edge);
1045 /* delete first segment from start to the first beamer */
1046 DrawLaserExt(start_edge, last_num_edges - start_edge, DL_LASER_DISABLED);
1050 DrawLaserExt(start_edge, laser.num_edges - start_edge, mode);
1053 boolean HitElement(int element, int hit_mask)
1055 if (HitOnlyAnEdge(element, hit_mask))
1058 if (IS_MOVING(ELX, ELY) || IS_BLOCKED(ELX, ELY))
1059 element = MovingOrBlocked2Element_MM(ELX, ELY);
1062 printf("HitElement (1): element == %d\n", element);
1066 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1067 printf("HitElement (%d): EXACT MATCH @ (%d, %d)\n", element, ELX, ELY);
1069 printf("HitElement (%d): FUZZY MATCH @ (%d, %d)\n", element, ELX, ELY);
1072 AddDamagedField(ELX, ELY);
1075 if (ELX != (LX + 5 * XS) / TILEX ||
1076 ELY != (LY + 5 * YS) / TILEY)
1086 /* this is more precise: check if laser would go through the center */
1087 if ((ELX * TILEX + 14 - LX) * YS != (ELY * TILEY + 14 - LY) * XS)
1089 /* skip the whole element before continuing the scan */
1095 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1097 if (LX/TILEX > ELX || LY/TILEY > ELY)
1099 /* skipping scan positions to the right and down skips one scan
1100 position too much, because this is only the top left scan position
1101 of totally four scan positions (plus one to the right, one to the
1102 bottom and one to the bottom right) */
1113 printf("HitElement (2): element == %d\n", element);
1116 if (LX + 5 * XS < 0 ||
1126 printf("HitElement (3): element == %d\n", element);
1129 if (IS_POLAR(element) &&
1130 ((element - EL_POLAR_START) % 2 ||
1131 (element - EL_POLAR_START) / 2 != laser.current_angle % 8))
1133 PlaySoundStereo(SND_KINK, ST(ELX));
1134 laser.num_damages--;
1139 if (IS_POLAR_CROSS(element) &&
1140 (element - EL_POLAR_CROSS_START) != laser.current_angle % 4)
1142 PlaySoundStereo(SND_KINK, ST(ELX));
1143 laser.num_damages--;
1148 if (!IS_BEAMER(element) &&
1149 !IS_FIBRE_OPTIC(element) &&
1150 !IS_GRID_WOOD(element) &&
1151 element != EL_FUEL_EMPTY)
1154 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1155 printf("EXACT MATCH @ (%d, %d)\n", ELX, ELY);
1157 printf("FUZZY MATCH @ (%d, %d)\n", ELX, ELY);
1160 LX = ELX * TILEX + 14;
1161 LY = ELY * TILEY + 14;
1162 AddLaserEdge(LX, LY);
1165 if (IS_MIRROR(element) ||
1166 IS_MIRROR_FIXED(element) ||
1167 IS_POLAR(element) ||
1168 IS_POLAR_CROSS(element) ||
1169 IS_DF_MIRROR(element) ||
1170 IS_DF_MIRROR_AUTO(element) ||
1171 element == EL_PRISM ||
1172 element == EL_REFRACTOR)
1174 int current_angle = laser.current_angle;
1177 laser.num_damages--;
1178 AddDamagedField(ELX, ELY);
1179 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1182 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1184 if (IS_MIRROR(element) ||
1185 IS_MIRROR_FIXED(element) ||
1186 IS_DF_MIRROR(element) ||
1187 IS_DF_MIRROR_AUTO(element))
1188 laser.current_angle = get_mirrored_angle(laser.current_angle,
1189 get_element_angle(element));
1191 if (element == EL_PRISM || element == EL_REFRACTOR)
1192 laser.current_angle = RND(16);
1194 XS = 2 * Step[laser.current_angle].x;
1195 YS = 2 * Step[laser.current_angle].y;
1197 if (!IS_22_5_ANGLE(laser.current_angle)) /* 90° or 45° angle */
1202 LX += step_size * XS;
1203 LY += step_size * YS;
1206 /* draw sparkles on mirror */
1207 if ((IS_MIRROR(element) || IS_MIRROR_FIXED(element)) &&
1208 current_angle != laser.current_angle)
1210 MoveSprite(vp, &Pfeil[2], 4 + 16 * ELX, 5 + 16 * ELY + 1);
1214 if ((!IS_POLAR(element) && !IS_POLAR_CROSS(element)) &&
1215 current_angle != laser.current_angle)
1216 PlaySoundStereo(SND_LASER, ST(ELX));
1219 (get_opposite_angle(laser.current_angle) ==
1220 laser.damage[laser.num_damages - 1].angle ? TRUE : FALSE);
1222 return (laser.overloaded ? TRUE : FALSE);
1225 if (element == EL_FUEL_FULL)
1227 laser.stops_inside_element = TRUE;
1232 if (element == EL_BOMB || element == EL_MINE)
1234 PlaySoundStereo(SND_KINK, ST(ELX));
1236 if (element == EL_MINE)
1237 laser.overloaded = TRUE;
1240 if (element == EL_KETTLE ||
1241 element == EL_CELL ||
1242 element == EL_KEY ||
1243 element == EL_LIGHTBALL ||
1244 element == EL_PACMAN ||
1247 if (!IS_PACMAN(element))
1250 if (element == EL_PACMAN)
1253 if (element == EL_KETTLE || element == EL_CELL)
1256 if (game_mm.kettles_still_needed)
1257 DrawText(DX_KETTLES, DY_KETTLES,
1258 int2str(--game_mm.kettles_still_needed, 3), FONT_TEXT_2);
1262 if (game_mm.kettles_still_needed == 0)
1265 static int xy[4][2] =
1273 PlaySoundStereo(SND_KLING, ST(ELX));
1275 for(y=0; y<lev_fieldy; y++)
1277 for(x=0; x<lev_fieldx; x++)
1279 /* initiate opening animation of exit door */
1280 if (Feld[x][y] == EL_EXIT_CLOSED)
1281 Feld[x][y] = EL_EXIT_OPENING;
1283 /* remove field that blocks receiver */
1284 if (IS_RECEIVER(Feld[x][y]))
1286 int phase = Feld[x][y] - EL_RECEIVER_START;
1287 int blocking_x, blocking_y;
1289 blocking_x = x + xy[phase][0];
1290 blocking_y = y + xy[phase][1];
1292 if (IN_LEV_FIELD(blocking_x, blocking_y))
1294 Feld[blocking_x][blocking_y] = EL_EMPTY;
1295 DrawField_MM(blocking_x, blocking_y);
1301 DrawLaser(0, DL_LASER_ENABLED);
1304 else if (element == EL_KEY)
1306 else if (element == EL_LIGHTBALL)
1308 else if (IS_PACMAN(element))
1310 DeletePacMan(ELX, ELY);
1317 if (element == EL_LIGHTBULB_OFF || element == EL_LIGHTBULB_ON)
1319 PlaySoundStereo(SND_KINK, ST(ELX));
1321 DrawLaser(0, DL_LASER_ENABLED);
1323 if (Feld[ELX][ELY] == EL_LIGHTBULB_OFF)
1325 Feld[ELX][ELY] = EL_LIGHTBULB_ON;
1326 game_mm.lights_still_needed--;
1330 Feld[ELX][ELY] = EL_LIGHTBULB_OFF;
1331 game_mm.lights_still_needed++;
1334 DrawField_MM(ELX, ELY);
1335 DrawLaser(0, DL_LASER_ENABLED);
1340 laser.stops_inside_element = TRUE;
1346 printf("HitElement (4): element == %d\n", element);
1349 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1350 laser.num_beamers < MAX_NUM_BEAMERS &&
1351 laser.beamer[BEAMER_NR(element)][1].num)
1353 int beamer_angle = get_element_angle(element);
1354 int beamer_nr = BEAMER_NR(element);
1358 printf("HitElement (BEAMER): element == %d\n", element);
1361 laser.num_damages--;
1363 if (IS_FIBRE_OPTIC(element) ||
1364 laser.current_angle == get_opposite_angle(beamer_angle))
1368 LX = ELX * TILEX + 14;
1369 LY = ELY * TILEY + 14;
1370 AddLaserEdge(LX, LY);
1371 AddDamagedField(ELX, ELY);
1372 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1375 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1377 pos = (ELX == laser.beamer[beamer_nr][0].x &&
1378 ELY == laser.beamer[beamer_nr][0].y ? 1 : 0);
1379 ELX = laser.beamer[beamer_nr][pos].x;
1380 ELY = laser.beamer[beamer_nr][pos].y;
1381 LX = ELX * TILEX + 14;
1382 LY = ELY * TILEY + 14;
1384 if (IS_BEAMER(element))
1386 laser.current_angle = get_element_angle(Feld[ELX][ELY]);
1387 XS = 2 * Step[laser.current_angle].x;
1388 YS = 2 * Step[laser.current_angle].y;
1391 laser.beamer_edge[laser.num_beamers] = laser.num_edges;
1392 AddLaserEdge(LX, LY);
1393 AddDamagedField(ELX, ELY);
1394 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1397 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1399 if (laser.current_angle == (laser.current_angle >> 1) << 1)
1404 LX += step_size * XS;
1405 LY += step_size * YS;
1407 laser.num_beamers++;
1416 boolean HitOnlyAnEdge(int element, int hit_mask)
1418 /* check if the laser hit only the edge of an element and, if so, go on */
1421 printf("LX, LY, hit_mask == %d, %d, %d\n", LX, LY, hit_mask);
1424 if ((hit_mask == HIT_MASK_TOPLEFT ||
1425 hit_mask == HIT_MASK_TOPRIGHT ||
1426 hit_mask == HIT_MASK_BOTTOMLEFT ||
1427 hit_mask == HIT_MASK_BOTTOMRIGHT) &&
1428 laser.current_angle % 4) /* angle is not 90° */
1432 if (hit_mask == HIT_MASK_TOPLEFT)
1437 else if (hit_mask == HIT_MASK_TOPRIGHT)
1442 else if (hit_mask == HIT_MASK_BOTTOMLEFT)
1447 else /* (hit_mask == HIT_MASK_BOTTOMRIGHT) */
1453 AddDamagedField((LX + 2 * dx) / TILEX, (LY + 2 * dy) / TILEY);
1458 printf("[HitOnlyAnEdge() == TRUE]\n");
1465 printf("[HitOnlyAnEdge() == FALSE]\n");
1471 boolean HitPolarizer(int element, int hit_mask)
1473 if (HitOnlyAnEdge(element, hit_mask))
1476 if (IS_DF_GRID(element))
1478 int grid_angle = get_element_angle(element);
1481 printf("HitPolarizer: angle: grid == %d, laser == %d\n",
1482 grid_angle, laser.current_angle);
1485 AddLaserEdge(LX, LY);
1486 AddDamagedField(ELX, ELY);
1489 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1491 if (laser.current_angle == grid_angle ||
1492 laser.current_angle == get_opposite_angle(grid_angle))
1497 if (!IS_22_5_ANGLE(laser.current_angle)) /* 90° or 45° angle */
1502 LX += step_size * XS;
1503 LY += step_size * YS;
1506 /* skip the whole element before continuing the scan */
1512 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1514 if (LX/TILEX > ELX || LY/TILEY > ELY)
1516 /* skipping scan positions to the right and down skips one scan
1517 position too much, because this is only the top left scan position
1518 of totally four scan positions (plus one to the right, one to the
1519 bottom and one to the bottom right) */
1526 AddLaserEdge(LX, LY);
1532 printf("HitPolarizer: LX, LY == %d, %d [%d, %d] [%d, %d]\n",
1534 LX / TILEX, LY / TILEY,
1535 LX % TILEX, LY % TILEY);
1540 else if (IS_GRID_STEEL_FIXED(element) || IS_GRID_STEEL_AUTO(element))
1541 return HitReflectingWalls(element, hit_mask);
1543 return HitAbsorbingWalls(element, hit_mask);
1545 else if (IS_GRID_STEEL(element))
1546 return HitReflectingWalls(element, hit_mask);
1547 else /* IS_GRID_WOOD */
1548 return HitAbsorbingWalls(element, hit_mask);
1553 boolean HitBlock(int element, int hit_mask)
1555 boolean check = FALSE;
1557 if ((element == EL_GATE_STONE || element == EL_GATE_WOOD) &&
1558 game_mm.num_keys == 0)
1561 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1564 int ex = ELX * TILEX + 14;
1565 int ey = ELY * TILEY + 14;
1574 if ((x == ex || x == ex + 1) && (y == ey || y == ey + 1))
1579 if (check && (element == EL_BLOCK_WOOD || element == EL_GATE_WOOD))
1580 return HitAbsorbingWalls(element, hit_mask);
1584 AddLaserEdge(LX - XS, LY - YS);
1585 AddDamagedField(ELX, ELY);
1588 Box[ELX][ELY] = laser.num_edges;
1590 return HitReflectingWalls(element, hit_mask);
1593 if (element == EL_GATE_STONE || element == EL_GATE_WOOD)
1595 int xs = XS / 2, ys = YS / 2;
1596 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1597 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1599 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1600 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1602 laser.overloaded = (element == EL_GATE_STONE);
1606 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1607 (hit_mask == HIT_MASK_TOP ||
1608 hit_mask == HIT_MASK_LEFT ||
1609 hit_mask == HIT_MASK_RIGHT ||
1610 hit_mask == HIT_MASK_BOTTOM))
1611 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1612 hit_mask == HIT_MASK_BOTTOM),
1613 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1614 hit_mask == HIT_MASK_RIGHT));
1615 AddLaserEdge(LX, LY);
1620 if (element == EL_GATE_STONE && Box[ELX][ELY])
1622 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
1634 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1636 int xs = XS / 2, ys = YS / 2;
1637 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1638 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1640 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1641 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1643 laser.overloaded = (element == EL_BLOCK_STONE);
1648 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1649 (hit_mask == HIT_MASK_TOP ||
1650 hit_mask == HIT_MASK_LEFT ||
1651 hit_mask == HIT_MASK_RIGHT ||
1652 hit_mask == HIT_MASK_BOTTOM))
1653 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1654 hit_mask == HIT_MASK_BOTTOM),
1655 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1656 hit_mask == HIT_MASK_RIGHT));
1657 AddDamagedField(ELX, ELY);
1659 LX = ELX * TILEX + 14;
1660 LY = ELY * TILEY + 14;
1661 AddLaserEdge(LX, LY);
1663 laser.stops_inside_element = TRUE;
1671 boolean HitLaserSource(int element, int hit_mask)
1673 if (HitOnlyAnEdge(element, hit_mask))
1676 PlaySoundStereo(SND_AUTSCH, ST(ELX));
1677 laser.overloaded = TRUE;
1682 boolean HitLaserDestination(int element, int hit_mask)
1684 if (HitOnlyAnEdge(element, hit_mask))
1687 if (element != EL_EXIT_OPEN &&
1688 !(IS_RECEIVER(element) &&
1689 game_mm.kettles_still_needed == 0 &&
1690 laser.current_angle == get_opposite_angle(get_element_angle(element))))
1692 PlaySoundStereo(SND_HOLZ, ST(ELX));
1696 if (IS_RECEIVER(element) ||
1697 (IS_22_5_ANGLE(laser.current_angle) &&
1698 (ELX != (LX + 6 * XS) / TILEX ||
1699 ELY != (LY + 6 * YS) / TILEY ||
1708 LX = ELX * TILEX + 14;
1709 LY = ELY * TILEY + 14;
1711 laser.stops_inside_element = TRUE;
1714 AddLaserEdge(LX, LY);
1715 AddDamagedField(ELX, ELY);
1717 if (game_mm.lights_still_needed == 0)
1718 game_mm.level_solved = TRUE;
1723 boolean HitReflectingWalls(int element, int hit_mask)
1725 /* check if laser hits side of a wall with an angle that is not 90° */
1726 if (!IS_90_ANGLE(laser.current_angle) && (hit_mask == HIT_MASK_TOP ||
1727 hit_mask == HIT_MASK_LEFT ||
1728 hit_mask == HIT_MASK_RIGHT ||
1729 hit_mask == HIT_MASK_BOTTOM))
1731 PlaySoundStereo(SND_HUI, ST(ELX));
1734 if (!IS_DF_GRID(element))
1735 AddLaserEdge(LX, LY);
1737 /* check if laser hits wall with an angle of 45° */
1738 if (!IS_22_5_ANGLE(laser.current_angle))
1740 if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1743 laser.current_angle = get_mirrored_angle(laser.current_angle,
1746 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1749 laser.current_angle = get_mirrored_angle(laser.current_angle,
1753 AddLaserEdge(LX, LY);
1754 XS = 2 * Step[laser.current_angle].x;
1755 YS = 2 * Step[laser.current_angle].y;
1759 else if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1761 laser.current_angle = get_mirrored_angle(laser.current_angle,
1766 if (!IS_DF_GRID(element))
1767 AddLaserEdge(LX, LY);
1772 if (!IS_DF_GRID(element))
1773 AddLaserEdge(LX, LY + YS / 2);
1776 if (!IS_DF_GRID(element))
1777 AddLaserEdge(LX, LY);
1780 YS = 2 * Step[laser.current_angle].y;
1784 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1786 laser.current_angle = get_mirrored_angle(laser.current_angle,
1791 if (!IS_DF_GRID(element))
1792 AddLaserEdge(LX, LY);
1797 if (!IS_DF_GRID(element))
1798 AddLaserEdge(LX + XS / 2, LY);
1801 if (!IS_DF_GRID(element))
1802 AddLaserEdge(LX, LY);
1805 XS = 2 * Step[laser.current_angle].x;
1811 /* reflection at the edge of reflecting DF style wall */
1812 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1814 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1815 hit_mask == HIT_MASK_TOPRIGHT) ||
1816 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1817 hit_mask == HIT_MASK_TOPLEFT) ||
1818 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1819 hit_mask == HIT_MASK_BOTTOMLEFT) ||
1820 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1821 hit_mask == HIT_MASK_BOTTOMRIGHT))
1824 (hit_mask == HIT_MASK_TOPRIGHT || hit_mask == HIT_MASK_BOTTOMLEFT ?
1825 ANG_MIRROR_135 : ANG_MIRROR_45);
1827 PlaySoundStereo(SND_HUI, ST(ELX));
1828 AddDamagedField(ELX, ELY);
1829 AddLaserEdge(LX, LY);
1831 laser.current_angle = get_mirrored_angle(laser.current_angle,
1838 AddLaserEdge(LX, LY);
1844 /* reflection inside an edge of reflecting DF style wall */
1845 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1847 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1848 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT)) ||
1849 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1850 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMRIGHT)) ||
1851 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1852 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT)) ||
1853 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1854 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPLEFT)))
1857 (hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT) ||
1858 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT) ?
1859 ANG_MIRROR_135 : ANG_MIRROR_45);
1861 PlaySoundStereo(SND_HUI, ST(ELX));
1863 AddDamagedField(ELX, ELY);
1865 AddLaserEdge(LX - XS, LY - YS);
1866 AddLaserEdge(LX - XS + (ABS(XS) == 4 ? XS/2 : 0),
1867 LY - YS + (ABS(YS) == 4 ? YS/2 : 0));
1869 laser.current_angle = get_mirrored_angle(laser.current_angle,
1876 AddLaserEdge(LX, LY);
1882 /* check if laser hits DF style wall with an angle of 90° */
1883 if (IS_DF_WALL(element) && IS_90_ANGLE(laser.current_angle))
1885 if ((IS_HORIZ_ANGLE(laser.current_angle) &&
1886 (!(hit_mask & HIT_MASK_TOP) || !(hit_mask & HIT_MASK_BOTTOM))) ||
1887 (IS_VERT_ANGLE(laser.current_angle) &&
1888 (!(hit_mask & HIT_MASK_LEFT) || !(hit_mask & HIT_MASK_RIGHT))))
1890 static int last_LX = 0, last_LY = 0, last_hit_mask = 0;
1892 /* laser at last step touched nothing or the same side of the wall */
1893 if (LX != last_LX || LY != last_LY || hit_mask == last_hit_mask)
1895 AddDamagedField(ELX, ELY);
1901 last_hit_mask = hit_mask;
1908 if (!HitOnlyAnEdge(element, hit_mask))
1910 laser.overloaded = TRUE;
1917 boolean HitAbsorbingWalls(int element, int hit_mask)
1919 if (HitOnlyAnEdge(element, hit_mask))
1923 (hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT))
1925 AddLaserEdge(LX - XS, LY - YS);
1931 (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM))
1933 AddLaserEdge(LX - XS, LY - YS);
1938 if (IS_WALL_WOOD(element) ||
1939 IS_DF_WALL_WOOD(element) ||
1940 IS_GRID_WOOD(element) ||
1941 IS_GRID_WOOD_FIXED(element) ||
1942 IS_GRID_WOOD_AUTO(element) ||
1943 element == EL_FUSE_ON ||
1944 element == EL_BLOCK_WOOD ||
1945 element == EL_GATE_WOOD)
1947 PlaySoundStereo(SND_HOLZ, ST(ELX));
1951 if (IS_WALL_ICE(element))
1955 mask = (LX + XS) / MINI_TILEX - ELX * 2 + 1; /* Quadrant (horizontal) */
1956 mask <<= (((LY + YS) / MINI_TILEY - ELY * 2) > 0) * 2; /* || (vertical) */
1958 /* check if laser hits wall with an angle of 90° */
1959 if (IS_90_ANGLE(laser.current_angle))
1960 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1962 if (mask == 1 || mask == 2 || mask == 4 || mask == 8)
1968 if (mask == (1 << i) && (XS > 0) == (i % 2) && (YS > 0) == (i / 2))
1969 mask = 15 - (8 >> i);
1970 else if (ABS(XS) == 4 &&
1972 (XS > 0) == (i % 2) &&
1973 (YS < 0) == (i / 2))
1974 mask = 3 + (i / 2) * 9;
1975 else if (ABS(YS) == 4 &&
1977 (XS < 0) == (i % 2) &&
1978 (YS > 0) == (i / 2))
1979 mask = 5 + (i % 2) * 5;
1983 laser.wall_mask = mask;
1985 else if (IS_WALL_AMOEBA(element))
1987 int elx = (LX - 2 * XS) / TILEX;
1988 int ely = (LY - 2 * YS) / TILEY;
1989 int element2 = Feld[elx][ely];
1992 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element2))
1994 laser.dest_element = EL_EMPTY;
2001 mask = (LX - 2 * XS) / 16 - ELX * 2 + 1;
2002 mask <<= ((LY - 2 * YS) / 16 - ELY * 2) * 2;
2004 if (IS_90_ANGLE(laser.current_angle))
2005 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
2007 laser.dest_element = element2 | EL_WALL_AMOEBA;
2009 laser.wall_mask = mask;
2015 void OpenExit(int x, int y)
2019 if (!MovDelay[x][y]) /* next animation frame */
2020 MovDelay[x][y] = 4 * delay;
2022 if (MovDelay[x][y]) /* wait some time before next frame */
2027 phase = MovDelay[x][y] / delay;
2028 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2029 DrawGraphic_MM(x, y, EL_EXIT_OPEN - phase);
2031 if (!MovDelay[x][y])
2033 Feld[x][y] = EL_EXIT_OPEN;
2039 void OpenSurpriseBall(int x, int y)
2043 if (!MovDelay[x][y]) /* next animation frame */
2044 MovDelay[x][y] = 50 * delay;
2046 if (MovDelay[x][y]) /* wait some time before next frame */
2054 phase = MovDelay[x][y] / delay;
2056 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2059 int graphic = el2gfx(Store[x][y]);
2061 int dx = RND(26), dy = RND(26);
2063 getGraphicSource(graphic, 0, &bitmap, &gx, &gy);
2064 BlitBitmap(bitmap, drawto, gx + dx, gy + dy, 6, 6,
2065 SX + x * TILEX + dx, SY + y * TILEY + dy);
2066 MarkTileDirty(x, y);
2069 if (!MovDelay[x][y])
2071 Feld[x][y] = Store[x][y];
2080 void MeltIce(int x, int y)
2085 if (!MovDelay[x][y]) /* next animation frame */
2086 MovDelay[x][y] = frames * delay;
2088 if (MovDelay[x][y]) /* wait some time before next frame */
2091 int wall_mask = Store2[x][y];
2092 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_ICE;
2095 phase = frames - MovDelay[x][y] / delay - 1;
2097 if (!MovDelay[x][y])
2101 Feld[x][y] = real_element & (wall_mask ^ 0xFF);
2102 Store[x][y] = Store2[x][y] = 0;
2104 DrawWalls_MM(x, y, Feld[x][y]);
2106 if (Feld[x][y] == EL_WALL_ICE)
2107 Feld[x][y] = EL_EMPTY;
2109 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
2110 if (laser.damage[i].is_mirror)
2114 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
2116 DrawLaser(0, DL_LASER_DISABLED);
2120 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2122 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2124 laser.redraw = TRUE;
2129 void GrowAmoeba(int x, int y)
2134 if (!MovDelay[x][y]) /* next animation frame */
2135 MovDelay[x][y] = frames * delay;
2137 if (MovDelay[x][y]) /* wait some time before next frame */
2140 int wall_mask = Store2[x][y];
2141 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_AMOEBA;
2144 phase = MovDelay[x][y] / delay;
2146 if (!MovDelay[x][y])
2148 Feld[x][y] = real_element;
2149 Store[x][y] = Store2[x][y] = 0;
2151 DrawWalls_MM(x, y, Feld[x][y]);
2152 DrawLaser(0, DL_LASER_ENABLED);
2154 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2155 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2159 static void Explode_MM(int x, int y, int phase, int mode)
2161 int num_phase = 9, delay = 2;
2162 int last_phase = num_phase * delay;
2163 int half_phase = (num_phase / 2) * delay;
2165 int first_phase_after_start = EX_PHASE_START + 1;
2168 laser.redraw = TRUE;
2170 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2172 int center_element = Feld[x][y];
2174 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2176 /* put moving element to center field (and let it explode there) */
2177 center_element = MovingOrBlocked2Element_MM(x, y);
2178 RemoveMovingField_MM(x, y);
2179 Feld[x][y] = center_element;
2182 if (center_element == EL_BOMB || IS_MCDUFFIN(center_element))
2183 Store[x][y] = center_element;
2185 Store[x][y] = EL_EMPTY;
2186 Store2[x][y] = mode;
2187 Feld[x][y] = EL_EXPLODING_OPAQUE;
2188 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2194 Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
2196 if (phase == half_phase)
2198 Feld[x][y] = EL_EXPLODING_TRANSP;
2200 if (x == ELX && y == ELY)
2204 if (phase == last_phase)
2210 if (Store[x][y] == EL_BOMB)
2212 laser.num_damages--;
2213 DrawLaser(0, DL_LASER_DISABLED);
2214 laser.num_edges = 0;
2216 Bang_MM(laser.start_edge.x, laser.start_edge.y);
2217 Store[x][y] = EL_EMPTY;
2219 else if (IS_MCDUFFIN(Store[x][y]))
2221 game_mm.game_over = TRUE;
2222 game_mm.game_over_cause = GAME_OVER_BOMB;
2223 Store[x][y] = EL_EMPTY;
2227 element = Feld[x][y] = Store[x][y];
2229 Store[x][y] = Store2[x][y] = 0;
2230 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2231 InitField(x, y, FALSE);
2234 else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2236 int graphic = GFX_EXPLOSION_START;
2237 int graphic_phase = (phase / delay - 1);
2239 if (Store2[x][y] == EX_KETTLE)
2241 if (graphic_phase < 3)
2242 graphic = GFX_EXPLOSION_KETTLE;
2243 else if (graphic_phase < 5)
2245 graphic = GFX_EXPLOSION_LAST;
2246 graphic_phase -= graphic_phase;
2250 graphic = GFX_EMPTY;
2254 else if (Store2[x][y] == EX_SHORT)
2256 if (graphic_phase < 4)
2257 graphic = GFX_EXPLOSION_SHORT;
2260 graphic = GFX_EMPTY;
2265 DrawGraphic_MM(x, y, graphic + graphic_phase);
2269 static void Bang_MM(int x, int y)
2271 int element = Feld[x][y];
2272 int mode = EX_NORMAL;
2275 DrawLaser(0, DL_LASER_ENABLED);
2294 if (IS_PACMAN(element))
2295 PlaySoundStereo(SND_QUIEK, ST(x));
2296 else if (element == EL_BOMB || IS_MCDUFFIN(element))
2297 PlaySoundStereo(SND_ROAAAR, ST(x));
2298 else if (element == EL_KEY)
2299 PlaySoundStereo(SND_KLING, ST(x));
2301 PlaySoundStereo((mode == EX_SHORT ? SND_WHOOSH : SND_KABUMM), ST(x));
2303 Explode_MM(x, y, EX_PHASE_START, mode);
2306 void TurnRound(int x, int y)
2318 { 0, 0 }, { 0, 0 }, { 0, 0 },
2323 int left, right, back;
2327 { MV_DOWN, MV_UP, MV_RIGHT },
2328 { MV_UP, MV_DOWN, MV_LEFT },
2330 { MV_LEFT, MV_RIGHT, MV_DOWN },
2331 { 0,0,0 }, { 0,0,0 }, { 0,0,0 },
2332 { MV_RIGHT, MV_LEFT, MV_UP }
2335 int element = Feld[x][y];
2336 int old_move_dir = MovDir[x][y];
2338 int left_dir = turn[old_move_dir].left;
2340 int right_dir = turn[old_move_dir].right;
2341 int back_dir = turn[old_move_dir].back;
2344 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
2346 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
2349 int left_x = x+left_dx, left_y = y+left_dy;
2351 int right_x = x+right_dx, right_y = y+right_dy;
2353 if (element == EL_PACMAN)
2356 boolean can_turn_left = FALSE;
2358 boolean can_turn_right = FALSE;
2361 if (IN_LEV_FIELD(left_x, left_y) &&
2362 IS_EATABLE4PACMAN(Feld[left_x][left_y]))
2363 can_turn_left = TRUE;
2365 if (IN_LEV_FIELD(right_x, right_y) &&
2366 IS_EATABLE4PACMAN(Feld[right_x][right_y]))
2367 can_turn_right = TRUE;
2370 MovDir[x][y] = right_dir;
2372 MovDir[x][y] = back_dir;
2378 static void StartMoving_MM(int x, int y)
2380 int element = Feld[x][y];
2385 if (CAN_MOVE(element))
2389 if (MovDelay[x][y]) /* wait some time before next movement */
2397 /* now make next step */
2399 Moving2Blocked_MM(x, y, &newx, &newy); /* get next screen position */
2401 if (element == EL_PACMAN &&
2402 IN_LEV_FIELD(newx, newy) && IS_EATABLE4PACMAN(Feld[newx][newy]) &&
2403 !ObjHit(newx, newy, HIT_POS_CENTER))
2405 Store[newx][newy] = Feld[newx][newy];
2406 Feld[newx][newy] = EL_EMPTY;
2407 DrawField_MM(newx, newy);
2409 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy) ||
2410 ObjHit(newx, newy, HIT_POS_CENTER))
2412 /* object was running against a wall */
2419 InitMovingField_MM(x, y, MovDir[x][y]);
2423 ContinueMoving_MM(x, y);
2426 static void ContinueMoving_MM(int x, int y)
2428 int element = Feld[x][y];
2429 int direction = MovDir[x][y];
2430 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2431 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2432 int horiz_move = (dx!=0);
2433 int newx = x + dx, newy = y + dy;
2434 int step = (horiz_move ? dx : dy) * TILEX / 8;
2436 MovPos[x][y] += step;
2438 if (ABS(MovPos[x][y]) >= TILEX) /* object reached its destination */
2440 Feld[x][y] = EL_EMPTY;
2441 Feld[newx][newy] = element;
2443 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2444 MovDelay[newx][newy] = 0;
2446 if (!CAN_MOVE(element))
2447 MovDir[newx][newy] = 0;
2450 DrawField_MM(newx, newy);
2452 Stop[newx][newy] = TRUE;
2454 if (element == EL_PACMAN)
2456 if (Store[newx][newy] == EL_BOMB)
2457 Bang_MM(newx, newy);
2459 if (IS_WALL_AMOEBA(Store[newx][newy]) &&
2460 (LX + 2 * XS) / TILEX == newx &&
2461 (LY + 2 * YS) / TILEY == newy)
2468 else /* still moving on */
2471 laser.redraw = TRUE;
2474 void ClickElement(int mx, int my, int button)
2476 static unsigned int click_delay = 0;
2477 static int click_delay_value = CLICK_DELAY_SHORT;
2478 static boolean new_button = TRUE;
2480 int x = (mx - SX) / TILEX, y = (my - SY) / TILEY;
2482 if (button == MB_RELEASED)
2485 click_delay_value = CLICK_DELAY_SHORT;
2487 /* release eventually hold auto-rotating mirror */
2488 RotateMirror(x, y, MB_RELEASED);
2493 if (!DelayReached(&click_delay, click_delay_value) && !new_button)
2496 if (button == MB_MIDDLEBUTTON) /* middle button has no function */
2499 if (!IN_PIX_FIELD(mx - SX, my - SY))
2502 if (Feld[x][y] == EL_EMPTY)
2505 element = Feld[x][y];
2507 if (IS_MIRROR(element) ||
2508 IS_BEAMER(element) ||
2509 IS_POLAR(element) ||
2510 IS_POLAR_CROSS(element) ||
2511 IS_DF_MIRROR(element) ||
2512 IS_DF_MIRROR_AUTO(element))
2514 RotateMirror(x, y, button);
2516 else if (IS_MCDUFFIN(element))
2518 if (!laser.fuse_off)
2520 DrawLaser(0, DL_LASER_DISABLED);
2526 element = get_rotated_element(element, BUTTON_ROTATION(button));
2527 laser.start_angle = get_element_angle(element);
2531 Feld[x][y] = element;
2536 if (!laser.fuse_off)
2539 else if (element == EL_FUSE_ON && laser.fuse_off)
2541 if (x != laser.fuse_x || y != laser.fuse_y)
2544 laser.fuse_off = FALSE;
2545 laser.fuse_x = laser.fuse_y = -1;
2547 DrawGraphic_MM(x, y, GFX_FUSE_ON);
2550 else if (element == EL_FUSE_ON && !laser.fuse_off && new_button)
2552 laser.fuse_off = TRUE;
2555 laser.overloaded = FALSE;
2557 DrawLaser(0, DL_LASER_DISABLED);
2558 DrawGraphic_MM(x, y, GFX_FUSE_OFF);
2560 else if (element == EL_LIGHTBALL)
2564 DrawLaser(0, DL_LASER_ENABLED);
2567 click_delay_value = (new_button ? CLICK_DELAY_LONG : CLICK_DELAY_SHORT);
2571 void RotateMirror(int x, int y, int button)
2573 static int hold_x = -1, hold_y = -1;
2575 if (button == MB_RELEASED)
2577 /* release eventually hold auto-rotating mirror */
2584 if (IS_MIRROR(Feld[x][y]) ||
2585 IS_POLAR_CROSS(Feld[x][y]) ||
2586 IS_POLAR(Feld[x][y]) ||
2587 IS_BEAMER(Feld[x][y]) ||
2588 IS_DF_MIRROR(Feld[x][y]) ||
2589 IS_GRID_STEEL_AUTO(Feld[x][y]) ||
2590 IS_GRID_WOOD_AUTO(Feld[x][y]))
2592 Feld[x][y] = get_rotated_element(Feld[x][y], BUTTON_ROTATION(button));
2594 else if (IS_DF_MIRROR_AUTO(Feld[x][y]))
2596 if (button == MB_LEFTBUTTON)
2598 /* left mouse button only for manual adjustment, no auto-rotating;
2599 freeze mirror for until mouse button released */
2603 else if (button == MB_RIGHTBUTTON && (hold_x != x || hold_y != y))
2604 Feld[x][y] = get_rotated_element(Feld[x][y], ROTATE_RIGHT);
2607 if (IS_GRID_STEEL_AUTO(Feld[x][y]) || IS_GRID_WOOD_AUTO(Feld[x][y]))
2609 int edge = Hit[x][y];
2615 DrawLaser(edge - 1, DL_LASER_DISABLED);
2619 else if (ObjHit(x, y, HIT_POS_CENTER))
2621 int edge = Hit[x][y];
2625 Error(ERR_WARN, "RotateMirror: inconsistent field Hit[][]!\n");
2629 DrawLaser(edge - 1, DL_LASER_DISABLED);
2636 if (ObjHit(x, y, HIT_POS_EDGE | HIT_POS_BETWEEN))
2641 if ((IS_BEAMER(Feld[x][y]) ||
2642 IS_POLAR(Feld[x][y]) ||
2643 IS_POLAR_CROSS(Feld[x][y])) && x == ELX && y == ELY)
2647 if (IS_BEAMER(Feld[x][y]))
2650 printf("TEST (%d, %d) [%d] [%d]\n",
2652 laser.beamer_edge, laser.beamer[1].num);
2662 DrawLaser(0, DL_LASER_ENABLED);
2666 void AutoRotateMirrors()
2668 static unsigned int rotate_delay = 0;
2671 if (!DelayReached(&rotate_delay, AUTO_ROTATE_DELAY))
2674 for (x=0; x<lev_fieldx; x++)
2676 for (y=0; y<lev_fieldy; y++)
2678 int element = Feld[x][y];
2680 if (IS_DF_MIRROR_AUTO(element) ||
2681 IS_GRID_WOOD_AUTO(element) ||
2682 IS_GRID_STEEL_AUTO(element) ||
2683 element == EL_REFRACTOR)
2684 RotateMirror(x, y, MB_RIGHTBUTTON);
2689 boolean ObjHit(int obx, int oby, int bits)
2696 if (bits & HIT_POS_CENTER)
2698 if (ReadPixel(drawto, SX + obx + 15, SY + oby + 15) == pen_ray)
2702 if (bits & HIT_POS_EDGE)
2705 if (ReadPixel(drawto,
2706 SX + obx + 31 * (i % 2),
2707 SY + oby + 31 * (i / 2)) == pen_ray)
2711 if (bits & HIT_POS_BETWEEN)
2714 if (ReadPixel(drawto,
2715 SX + 4 + obx + 22 * (i % 2),
2716 SY + 4 + oby + 22 * (i / 2)) == pen_ray)
2723 void DeletePacMan(int px, int py)
2729 if (game_mm.num_pacman <= 1)
2731 game_mm.num_pacman = 0;
2735 for(i=0; i<game_mm.num_pacman; i++)
2736 if (game_mm.pacman[i].x == px && game_mm.pacman[i].y == py)
2739 game_mm.num_pacman--;
2741 for(j=i; j<game_mm.num_pacman; j++)
2743 game_mm.pacman[j].x = game_mm.pacman[j + 1].x;
2744 game_mm.pacman[j].y = game_mm.pacman[j + 1].y;
2745 game_mm.pacman[j].dir = game_mm.pacman[j + 1].dir;
2749 void ColorCycling(void)
2751 static int CC, Cc = 0;
2753 static int color, old = 0xF00, new = 0x010, mult = 1;
2754 static unsigned short red, green, blue;
2756 if (color_status == STATIC_COLORS)
2761 if (CC < Cc || CC > Cc + 50)
2765 color = old + new * mult;
2771 if (ABS(mult) == 16)
2780 red = 0x0e00 * ((color & 0xF00) >> 8);
2781 green = 0x0e00 * ((color & 0x0F0) >> 4);
2782 blue = 0x0e00 * ((color & 0x00F));
2783 SetRGB(pen_magicolor[0], red, green, blue);
2785 red = 0x1111 * ((color & 0xF00) >> 8);
2786 green = 0x1111 * ((color & 0x0F0) >> 4);
2787 blue = 0x1111 * ((color & 0x00F));
2788 SetRGB(pen_magicolor[1], red, green, blue);
2792 void GameActions_MM(byte action[MAX_PLAYERS], boolean warp_mode)
2794 static unsigned int action_delay = 0;
2795 static unsigned int pacman_delay = 0;
2796 static unsigned int energy_delay = 0;
2797 static unsigned int overload_delay = 0;
2799 unsigned short color_scale = 0xFFFF / 15;
2807 WaitUntilDelayReached(&action_delay, GameFrameDelay);
2809 if (!DelayReached(&action_delay, GameFrameDelay))
2811 if (!PendingEvent()) /* delay only if no pending events */
2817 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
2820 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
2822 element = Feld[x][y];
2824 if (!IS_MOVING(x, y) && CAN_MOVE(element))
2825 StartMoving_MM(x, y);
2826 else if (IS_MOVING(x, y))
2827 ContinueMoving_MM(x, y);
2828 else if (IS_EXPLODING(element))
2829 Explode_MM(x, y, Frame[x][y], EX_NORMAL);
2830 else if (element == EL_EXIT_OPENING)
2832 else if (element == EL_GRAY_BALL_OPENING)
2833 OpenSurpriseBall(x, y);
2834 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_ICE)
2836 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_AMOEBA)
2840 AutoRotateMirrors();
2843 /* !!! CHANGE THIS: REDRAW ONLY WHEN NEEDED !!! */
2845 /* redraw after Explode_MM() ... */
2847 DrawLaser(0, DL_LASER_ENABLED);
2848 laser.redraw = FALSE;
2853 if (game_mm.num_pacman && DelayReached(&pacman_delay, 250))
2857 if (laser.num_damages > MAX_LASER_LEN && !laser.fuse_off)
2859 DrawLaser(0, DL_LASER_DISABLED);
2864 if (DelayReached(&energy_delay, 4000))
2866 game_mm.energy_left--;
2867 if (game_mm.energy_left >= 0)
2870 BlitBitmap(pix[PIX_DOOR], drawto,
2871 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
2872 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
2873 DX_ENERGY, DY_ENERGY);
2875 redraw_mask |= REDRAW_DOOR_1;
2877 else if (setup.time_limit)
2881 for(i=15; i>=0; i--)
2884 SetRGB(pen_ray, 0x0000, 0x0000, i * color_scale);
2886 pen_ray = GetPixelFromRGB(window,
2887 native_mm_level.laser_red * 0x11 * i,
2888 native_mm_level.laser_green * 0x11 * i,
2889 native_mm_level.laser_blue * 0x11 * i);
2890 DrawLaser(0, DL_LASER_ENABLED);
2895 StopSound(SND_WARNTON);
2898 DrawLaser(0, DL_LASER_DISABLED);
2899 game_mm.game_over = TRUE;
2900 game_mm.game_over_cause = GAME_OVER_NO_ENERGY;
2903 if (Request("Out of magic energy ! Play it again ?",
2904 REQ_ASK | REQ_STAY_CLOSED))
2910 game_status = MAINMENU;
2919 element = laser.dest_element;
2922 if (element != Feld[ELX][ELY])
2924 printf("element == %d, Feld[ELX][ELY] == %d\n",
2925 element, Feld[ELX][ELY]);
2929 if (!laser.overloaded && laser.overload_value == 0 &&
2930 element != EL_BOMB &&
2931 element != EL_MINE &&
2932 element != EL_BALL_GRAY &&
2933 element != EL_BLOCK_STONE &&
2934 element != EL_BLOCK_WOOD &&
2935 element != EL_FUSE_ON &&
2936 element != EL_FUEL_FULL &&
2937 !IS_WALL_ICE(element) &&
2938 !IS_WALL_AMOEBA(element))
2941 if (((laser.overloaded && laser.overload_value < MAX_LASER_OVERLOAD) ||
2942 (!laser.overloaded && laser.overload_value > 0)) &&
2943 DelayReached(&overload_delay, 60 + !laser.overloaded * 120))
2945 if (laser.overloaded)
2946 laser.overload_value++;
2948 laser.overload_value--;
2950 if (game_mm.cheat_no_overload)
2952 laser.overloaded = FALSE;
2953 laser.overload_value = 0;
2956 if (laser.overload_value < MAX_LASER_OVERLOAD - 8)
2958 int color_up = 0xFF * laser.overload_value / MAX_LASER_OVERLOAD;
2959 int color_down = 0xFF - color_up;
2962 SetRGB(pen_ray, (laser.overload_value / 6) * color_scale, 0x0000,
2963 (15 - (laser.overload_value / 6)) * color_scale);
2965 pen_ray = GetPixelFromRGB(window,
2966 (native_mm_level.laser_red ? 0xFF : color_up),
2967 (native_mm_level.laser_green ? color_down : 0x00),
2968 (native_mm_level.laser_blue ? color_down : 0x00));
2969 DrawLaser(0, DL_LASER_ENABLED);
2973 if (laser.overloaded)
2975 if (setup.sound_loops)
2976 PlaySoundExt(SND_WARNTON, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT, SND_CTRL_PLAY_LOOP);
2978 PlaySoundStereo(SND_WARNTON, SOUND_MAX_RIGHT);
2981 if (!laser.overloaded)
2982 StopSound(SND_WARNTON);
2984 if (laser.overloaded)
2987 BlitBitmap(pix[PIX_DOOR], drawto,
2988 DOOR_GFX_PAGEX4 + XX_OVERLOAD,
2989 DOOR_GFX_PAGEY1 + YY_OVERLOAD + OVERLOAD_YSIZE
2990 - laser.overload_value,
2991 OVERLOAD_XSIZE, laser.overload_value,
2992 DX_OVERLOAD, DY_OVERLOAD + OVERLOAD_YSIZE
2993 - laser.overload_value);
2995 redraw_mask |= REDRAW_DOOR_1;
3000 BlitBitmap(pix[PIX_DOOR], drawto,
3001 DOOR_GFX_PAGEX5 + XX_OVERLOAD, DOOR_GFX_PAGEY1 + YY_OVERLOAD,
3002 OVERLOAD_XSIZE, OVERLOAD_YSIZE - laser.overload_value,
3003 DX_OVERLOAD, DY_OVERLOAD);
3005 redraw_mask |= REDRAW_DOOR_1;
3008 if (laser.overload_value == MAX_LASER_OVERLOAD)
3012 for(i=15; i>=0; i--)
3015 SetRGB(pen_ray, i * color_scale, 0x0000, 0x0000);
3018 pen_ray = GetPixelFromRGB(window, 0x11 * i, 0x00, 0x00);
3019 DrawLaser(0, DL_LASER_ENABLED);
3024 DrawLaser(0, DL_LASER_DISABLED);
3025 game_mm.game_over = TRUE;
3026 game_mm.game_over_cause = GAME_OVER_OVERLOADED;
3029 if (Request("Magic spell hit Mc Duffin ! Play it again ?",
3030 REQ_ASK | REQ_STAY_CLOSED))
3036 game_status = MAINMENU;
3050 if (element == EL_BOMB && CT > 1500)
3052 if (game_mm.cheat_no_explosion)
3056 laser.num_damages--;
3057 DrawLaser(0, DL_LASER_DISABLED);
3058 laser.num_edges = 0;
3063 laser.dest_element = EL_EXPLODING_OPAQUE;
3067 laser.num_damages--;
3068 DrawLaser(0, DL_LASER_DISABLED);
3070 laser.num_edges = 0;
3071 Bang_MM(laser.start_edge.x, laser.start_edge.y);
3073 if (Request("Bomb killed Mc Duffin ! Play it again ?",
3074 REQ_ASK | REQ_STAY_CLOSED))
3080 game_status = MAINMENU;
3088 if (element == EL_FUSE_ON && CT > 500)
3090 laser.fuse_off = TRUE;
3093 DrawLaser(0, DL_LASER_DISABLED);
3094 DrawGraphic_MM(ELX, ELY, GFX_FUSE_OFF);
3097 if (element == EL_BALL_GRAY && CT > 1500)
3099 static int new_elements[] =
3102 EL_MIRROR_FIXED_START,
3104 EL_POLAR_CROSS_START,
3110 int num_new_elements = sizeof(new_elements) / sizeof(int);
3111 int new_element = new_elements[RND(num_new_elements)];
3113 Store[ELX][ELY] = new_element + RND(get_num_elements(new_element));
3114 Feld[ELX][ELY] = EL_GRAY_BALL_OPENING;
3116 /* !!! CHECK AGAIN: Laser on Polarizer !!! */
3127 element = EL_MIRROR_START + RND(16);
3133 element = (rnd == 0 ? EL_KETTLE : rnd == 1 ? EL_BOMB : EL_PRISM);
3140 element = (rnd == 0 ? EL_FUSE_ON :
3141 rnd >= 1 && rnd <= 4 ? EL_PACMAN_RIGHT + rnd - 1 :
3142 rnd >= 5 && rnd <= 20 ? EL_POLAR_START + rnd - 5 :
3143 rnd >= 21 && rnd <= 24 ? EL_POLAR_CROSS_START + rnd - 21 :
3144 EL_MIRROR_FIXED_START + rnd - 25);
3149 graphic = el2gfx(element);
3157 BlitBitmap(pix[PIX_BACK], drawto,
3158 SX + (graphic % GFX_PER_LINE) * TILEX + x,
3159 SY + (graphic / GFX_PER_LINE) * TILEY + y, 6, 6,
3160 SX + ELX * TILEX + x,
3161 SY + ELY * TILEY + y);
3163 MarkTileDirty(ELX, ELY);
3166 DrawLaser(0, DL_LASER_ENABLED);
3171 Feld[ELX][ELY] = element;
3172 DrawField_MM(ELX, ELY);
3175 printf("NEW ELEMENT: (%d, %d)\n", ELX, ELY);
3178 /* above stuff: GRAY BALL -> PRISM !!! */
3180 LX = ELX * TILEX + 14;
3181 LY = ELY * TILEY + 14;
3182 if (laser.current_angle == (laser.current_angle >> 1) << 1)
3189 laser.num_edges -= 2;
3190 laser.num_damages--;
3194 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
3195 if (laser.damage[i].is_mirror)
3199 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3201 DrawLaser(0, DL_LASER_DISABLED);
3203 DrawLaser(0, DL_LASER_DISABLED);
3209 printf("TEST ELEMENT: %d\n", Feld[0][0]);
3216 if (IS_WALL_ICE(element) && CT > 1000)
3218 PlaySoundStereo(SND_SLURP, ST(ELX));
3223 Feld[ELX][ELY] = Feld[ELX][ELY] - EL_WALL_ICE + EL_WALL_CHANGING;
3224 Store[ELX][ELY] = EL_WALL_ICE;
3225 Store2[ELX][ELY] = laser.wall_mask;
3227 laser.dest_element = Feld[ELX][ELY];
3241 Feld[ELX][ELY] &= (laser.wall_mask ^ 0xFF);
3245 DrawWallsAnimation_MM(ELX, ELY, Feld[ELX][ELY], phase, laser.wall_mask);
3250 if (Feld[ELX][ELY] == EL_WALL_ICE)
3251 Feld[ELX][ELY] = EL_EMPTY;
3255 LX = laser.edge[laser.num_edges].x - (SX + 2);
3256 LY = laser.edge[laser.num_edges].y - (SY + 2);
3259 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
3260 if (laser.damage[i].is_mirror)
3264 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3266 DrawLaser(0, DL_LASER_DISABLED);
3273 if (IS_WALL_AMOEBA(element) && CT > 1200)
3275 int k1, k2, k3, dx, dy, de, dm;
3276 int element2 = Feld[ELX][ELY];
3278 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element))
3281 for (i = laser.num_damages - 1; i>=0; i--)
3282 if (laser.damage[i].is_mirror)
3285 r = laser.num_edges;
3286 d = laser.num_damages;
3293 DrawLaser(laser.damage[k1].edge - 1, DL_LASER_DISABLED);
3296 DrawLaser(0, DL_LASER_ENABLED);
3299 x = laser.damage[k1].x;
3300 y = laser.damage[k1].y;
3306 if (laser.wall_mask & (1 << i))
3308 if (ReadPixel(drawto,
3309 SX + ELX * TILEX + 14 + (i % 2) * 2,
3310 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3312 if (ReadPixel(drawto,
3313 SX + ELX * TILEX + 31 * (i % 2),
3314 SY + ELY * TILEY + 14 + (i / 2) * 2) == pen_ray)
3323 if (laser.wall_mask & (1 << i))
3325 if (ReadPixel(drawto,
3326 SX + ELX * TILEX + 31 * (i % 2),
3327 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3334 if (laser.num_beamers > 0 ||
3335 k1 < 1 || k2 < 4 || k3 < 4 ||
3336 ReadPixel(drawto, SX + ELX * TILEX + 14, SY + ELY * TILEY + 14)
3339 laser.num_edges = r;
3340 laser.num_damages = d;
3341 DrawLaser(0, DL_LASER_DISABLED);
3344 Feld[ELX][ELY] = element | laser.wall_mask;
3347 de = Feld[ELX][ELY];
3348 dm = laser.wall_mask;
3354 int x = ELX, y = ELY;
3355 int wall_mask = laser.wall_mask;
3359 DrawLaser(0, DL_LASER_ENABLED);
3361 PlaySoundStereo(SND_AMOEBE, ST(dx));
3365 Feld[x][y] = Feld[x][y] - EL_WALL_AMOEBA + EL_WALL_CHANGING;
3366 Store[x][y] = EL_WALL_AMOEBA;
3367 Store2[x][y] = wall_mask;
3375 DrawWallsAnimation_MM(dx, dy, de, 4, dm);
3377 DrawLaser(0, DL_LASER_ENABLED);
3379 PlaySoundStereo(SND_AMOEBE, ST(dx));
3383 DrawWallsAnimation_MM(dx, dy, de, i, dm);
3388 DrawLaser(0, DL_LASER_ENABLED);
3393 if ((element == EL_BLOCK_WOOD || element == EL_BLOCK_STONE) &&
3394 laser.stops_inside_element && CT > 1500)
3399 if (ABS(XS) > ABS(YS))
3413 x = ELX + Step[k * 4].x;
3414 y = ELY + Step[k * 4].y;
3416 if (!IN_LEV_FIELD(x, y) || Feld[x][y] != EL_EMPTY)
3419 if (ObjHit(x, y, HIT_POS_CENTER | HIT_POS_EDGE | HIT_POS_BETWEEN))
3427 laser.overloaded = (element == EL_BLOCK_STONE);
3431 PlaySoundStereo(SND_BONG, ST(ELX));
3434 Feld[x][y] = element;
3436 DrawGraphic_MM(ELX, ELY, -1);
3439 if (element == EL_BLOCK_STONE && Box[ELX][ELY])
3441 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
3442 DrawLaser(laser.num_edges - 1, DL_LASER_ENABLED);
3450 if (element == EL_FUEL_FULL && CT > 200)
3452 for(i=game_mm.energy_left; i<=MAX_LASER_ENERGY; i+=2)
3455 BlitBitmap(pix[PIX_DOOR], drawto,
3456 DOOR_GFX_PAGEX4 + XX_ENERGY,
3457 DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
3458 ENERGY_XSIZE, i, DX_ENERGY,
3459 DY_ENERGY + ENERGY_YSIZE - i);
3462 redraw_mask |= REDRAW_DOOR_1;
3468 game_mm.energy_left = MAX_LASER_ENERGY;
3469 Feld[ELX][ELY] = EL_FUEL_EMPTY;
3470 DrawField_MM(ELX, ELY);
3472 DrawLaser(0, DL_LASER_ENABLED);
3483 int mx, my, ox, oy, nx, ny;
3487 if (++p >= game_mm.num_pacman)
3489 game_mm.pacman[p].dir--;
3493 game_mm.pacman[p].dir++;
3495 if (game_mm.pacman[p].dir > 4)
3496 game_mm.pacman[p].dir = 1;
3498 if (game_mm.pacman[p].dir % 2)
3501 my = game_mm.pacman[p].dir - 2;
3506 mx = 3 - game_mm.pacman[p].dir;
3509 ox = game_mm.pacman[p].x;
3510 oy = game_mm.pacman[p].y;
3513 element = Feld[nx][ny];
3514 if (nx < 0 || nx > 15 || ny < 0 || ny > 11)
3517 if (!IS_EATABLE4PACMAN(element))
3520 if (ObjHit(nx, ny, HIT_POS_CENTER))
3523 Feld[ox][oy] = EL_EMPTY;
3525 EL_PACMAN_RIGHT - 1 +
3526 (game_mm.pacman[p].dir - 1 +
3527 (game_mm.pacman[p].dir % 2) * 2);
3529 game_mm.pacman[p].x = nx;
3530 game_mm.pacman[p].y = ny;
3531 g = Feld[nx][ny] - EL_PACMAN_RIGHT;
3532 DrawGraphic_MM(ox, oy, GFX_EMPTY);
3534 if (element != EL_EMPTY)
3539 ox = SX + ox * TILEX;
3540 oy = SY + oy * TILEY;
3542 for(i=1; i<33; i+=2)
3545 // !!! temporary fix to compile -- change to game graphics !!!
3546 BlitBitmap(drawto, window,
3547 SX + g * TILEX, SY + 4 * TILEY, TILEX, TILEY,
3548 ox + i * mx, oy + i * my);
3550 BlitBitmap(pix[PIX_BACK], window,
3551 SX + g * TILEX, SY + 4 * TILEY, TILEX, TILEY,
3552 ox + i * mx, oy + i * my);
3560 Ct = Ct + Counter() - CT;
3562 DrawField_MM(nx, ny);
3565 if (!laser.fuse_off)
3567 DrawLaser(0, DL_LASER_ENABLED);
3569 if (ObjHit(nx, ny, HIT_POS_BETWEEN))
3571 AddDamagedField(nx, ny);
3572 laser.damage[laser.num_damages - 1].edge = 0;
3576 if (element == EL_BOMB)
3578 DeletePacMan(nx, ny);
3581 if (IS_WALL_AMOEBA(element) &&
3582 (LX + 2 * XS) / TILEX == nx &&
3583 (LY + 2 * YS) / TILEY == ny)
3595 boolean raise_level = FALSE;
3598 if (local_player->MovPos)
3601 local_player->LevelSolved = FALSE;
3604 if (game_mm.energy_left)
3606 if (setup.sound_loops)
3607 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT, SND_CTRL_PLAY_LOOP);
3609 while(game_mm.energy_left > 0)
3611 if (!setup.sound_loops)
3612 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3615 if (game_mm.energy_left > 0 && !(game_mm.energy_left % 10))
3616 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3621 game_mm.energy_left--;
3622 if (game_mm.energy_left >= 0)
3625 BlitBitmap(pix[PIX_DOOR], drawto,
3626 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
3627 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
3628 DX_ENERGY, DY_ENERGY);
3630 redraw_mask |= REDRAW_DOOR_1;
3637 if (setup.sound_loops)
3638 StopSound(SND_SIRR);
3640 else if (native_mm_level.time == 0) /* level without time limit */
3642 if (setup.sound_loops)
3643 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT, SND_CTRL_PLAY_LOOP);
3645 while(TimePlayed < 999)
3647 if (!setup.sound_loops)
3648 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3649 if (TimePlayed < 999 && !(TimePlayed % 10))
3650 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3651 if (TimePlayed < 900 && !(TimePlayed % 10))
3657 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
3664 if (setup.sound_loops)
3665 StopSound(SND_SIRR);
3672 CloseDoor(DOOR_CLOSE_1);
3674 Request("Level solved !", REQ_CONFIRM);
3676 if (level_nr == leveldir_current->handicap_level)
3678 leveldir_current->handicap_level++;
3679 SaveLevelSetup_SeriesInfo();
3682 if (level_editor_test_game)
3683 game_mm.score = -1; /* no highscore when playing from editor */
3684 else if (level_nr < leveldir_current->last_level)
3685 raise_level = TRUE; /* advance to next level */
3687 if ((hi_pos = NewHiScore_MM()) >= 0)
3689 game_status = HALLOFFAME;
3690 // DrawHallOfFame(hi_pos);
3696 game_status = MAINMENU;
3710 // LoadScore(level_nr);
3712 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
3713 game_mm.score < highscore[MAX_SCORE_ENTRIES - 1].Score)
3716 for (k=0; k<MAX_SCORE_ENTRIES; k++)
3718 if (game_mm.score > highscore[k].Score)
3720 /* player has made it to the hall of fame */
3722 if (k < MAX_SCORE_ENTRIES - 1)
3724 int m = MAX_SCORE_ENTRIES - 1;
3727 for (l=k; l<MAX_SCORE_ENTRIES; l++)
3728 if (!strcmp(setup.player_name, highscore[l].Name))
3730 if (m == k) /* player's new highscore overwrites his old one */
3736 strcpy(highscore[l].Name, highscore[l - 1].Name);
3737 highscore[l].Score = highscore[l - 1].Score;
3744 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3745 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3746 highscore[k].Score = game_mm.score;
3752 else if (!strncmp(setup.player_name, highscore[k].Name,
3753 MAX_PLAYER_NAME_LEN))
3754 break; /* player already there with a higher score */
3759 // if (position >= 0)
3760 // SaveScore(level_nr);
3765 static void InitMovingField_MM(int x, int y, int direction)
3767 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3768 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3770 MovDir[x][y] = direction;
3771 MovDir[newx][newy] = direction;
3772 if (Feld[newx][newy] == EL_EMPTY)
3773 Feld[newx][newy] = EL_BLOCKED;
3776 static void Moving2Blocked_MM(int x, int y, int *goes_to_x, int *goes_to_y)
3778 int direction = MovDir[x][y];
3779 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3780 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3786 static void Blocked2Moving_MM(int x, int y,
3787 int *comes_from_x, int *comes_from_y)
3789 int oldx = x, oldy = y;
3790 int direction = MovDir[x][y];
3792 if (direction == MV_LEFT)
3794 else if (direction == MV_RIGHT)
3796 else if (direction == MV_UP)
3798 else if (direction == MV_DOWN)
3801 *comes_from_x = oldx;
3802 *comes_from_y = oldy;
3805 static int MovingOrBlocked2Element_MM(int x, int y)
3807 int element = Feld[x][y];
3809 if (element == EL_BLOCKED)
3813 Blocked2Moving_MM(x, y, &oldx, &oldy);
3814 return Feld[oldx][oldy];
3821 static void RemoveField(int x, int y)
3823 Feld[x][y] = EL_EMPTY;
3830 static void RemoveMovingField_MM(int x, int y)
3832 int oldx = x, oldy = y, newx = x, newy = y;
3834 if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
3837 if (IS_MOVING(x, y))
3839 Moving2Blocked_MM(x, y, &newx, &newy);
3840 if (Feld[newx][newy] != EL_BLOCKED)
3843 else if (Feld[x][y] == EL_BLOCKED)
3845 Blocked2Moving_MM(x, y, &oldx, &oldy);
3846 if (!IS_MOVING(oldx, oldy))
3850 Feld[oldx][oldy] = EL_EMPTY;
3851 Feld[newx][newy] = EL_EMPTY;
3852 MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
3853 MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
3855 DrawLevelField_MM(oldx, oldy);
3856 DrawLevelField_MM(newx, newy);
3859 void PlaySoundLevel(int x, int y, int sound_nr)
3861 int sx = SCREENX(x), sy = SCREENY(y);
3863 int silence_distance = 8;
3865 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
3866 (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
3869 if (!IN_LEV_FIELD(x, y) ||
3870 sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
3871 sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
3874 volume = SOUND_MAX_VOLUME;
3877 stereo = (sx - SCR_FIELDX/2) * 12;
3879 stereo = SOUND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
3880 if (stereo > SOUND_MAX_RIGHT)
3881 stereo = SOUND_MAX_RIGHT;
3882 if (stereo < SOUND_MAX_LEFT)
3883 stereo = SOUND_MAX_LEFT;
3886 if (!IN_SCR_FIELD(sx, sy))
3888 int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
3889 int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
3891 volume -= volume * (dx > dy ? dx : dy) / silence_distance;
3894 PlaySoundExt(sound_nr, volume, stereo, SND_CTRL_PLAY_SOUND);
3897 static void RaiseScore_MM(int value)
3899 game_mm.score += value;
3901 DrawText(DX_SCORE, DY_SCORE, int2str(game_mm.score, 4),
3906 void RaiseScoreElement_MM(int element)
3911 RaiseScore_MM(native_mm_level.score[SC_PACMAN]);
3914 RaiseScore_MM(native_mm_level.score[SC_KEY]);