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_FIRST 12 /* delay (frames) after first click */
70 #define CLICK_DELAY 6 /* delay (frames) for pressed butten */
72 #define AUTO_ROTATE_DELAY CLICK_DELAY
73 #define INIT_GAME_ACTIONS_DELAY (ONE_SECOND_DELAY / GAME_FRAME_DELAY)
74 #define NUM_INIT_CYCLE_STEPS 16
75 #define PACMAN_MOVE_DELAY 12
76 #define ENERGY_DELAY (4 * ONE_SECOND_DELAY / GAME_FRAME_DELAY)
77 #define HEALTH_DEC_DELAY 3
78 #define HEALTH_INC_DELAY 9
79 #define HEALTH_DELAY(x) ((x) ? HEALTH_DEC_DELAY : HEALTH_INC_DELAY)
81 #define BEGIN_NO_HEADLESS \
83 boolean last_headless = program.headless; \
85 program.headless = FALSE; \
87 #define END_NO_HEADLESS \
88 program.headless = last_headless; \
91 /* forward declaration for internal use */
92 static int MovingOrBlocked2Element_MM(int, int);
93 static void Bang_MM(int, int);
94 static void RaiseScore_MM(int);
95 static void RemoveMovingField_MM(int, int);
96 static void InitMovingField_MM(int, int, int);
97 static void ContinueMoving_MM(int, int);
98 static void Moving2Blocked_MM(int, int, int *, int *);
100 /* bitmap for laser beam detection */
101 static Bitmap *laser_bitmap = NULL;
103 /* element masks for scanning pixels of MM elements */
104 static const char mm_masks[10][16][16 + 1] =
288 static int get_element_angle(int element)
290 int element_phase = get_element_phase(element);
292 if (IS_MIRROR_FIXED(element) ||
293 IS_MCDUFFIN(element) ||
295 IS_RECEIVER(element))
296 return 4 * element_phase;
298 return element_phase;
301 static int get_opposite_angle(int angle)
303 int opposite_angle = angle + ANG_RAY_180;
305 /* make sure "opposite_angle" is in valid interval [0, 15] */
306 return (opposite_angle + 16) % 16;
309 static int get_mirrored_angle(int laser_angle, int mirror_angle)
311 int reflected_angle = 16 - laser_angle + mirror_angle;
313 /* make sure "reflected_angle" is in valid interval [0, 15] */
314 return (reflected_angle + 16) % 16;
317 static void DrawLaserLines(struct XY *points, int num_points, int mode)
319 Pixel pixel_drawto = (mode == DL_LASER_ENABLED ? pen_ray : pen_bg);
320 Pixel pixel_buffer = (mode == DL_LASER_ENABLED ? WHITE_PIXEL : BLACK_PIXEL);
322 DrawLines(drawto, points, num_points, pixel_drawto);
326 DrawLines(laser_bitmap, points, num_points, pixel_buffer);
331 static boolean CheckLaserPixel(int x, int y)
337 pixel = ReadPixel(laser_bitmap, x, y);
341 return (pixel == WHITE_PIXEL);
344 static void InitMovDir_MM(int x, int y)
346 int element = Feld[x][y];
347 static int direction[3][4] =
349 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
350 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
351 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
356 case EL_PACMAN_RIGHT:
360 Feld[x][y] = EL_PACMAN;
361 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
369 static void InitField(int x, int y, boolean init_game)
371 int element = Feld[x][y];
376 Feld[x][y] = EL_EMPTY;
381 if (native_mm_level.auto_count_kettles)
382 game_mm.kettles_still_needed++;
385 case EL_LIGHTBULB_OFF:
386 game_mm.lights_still_needed++;
390 if (IS_MIRROR(element) ||
391 IS_BEAMER_OLD(element) ||
392 IS_BEAMER(element) ||
394 IS_POLAR_CROSS(element) ||
395 IS_DF_MIRROR(element) ||
396 IS_DF_MIRROR_AUTO(element) ||
397 IS_GRID_STEEL_AUTO(element) ||
398 IS_GRID_WOOD_AUTO(element) ||
399 IS_FIBRE_OPTIC(element))
401 if (IS_BEAMER_OLD(element))
403 Feld[x][y] = EL_BEAMER_BLUE_START + (element - EL_BEAMER_START);
404 element = Feld[x][y];
407 if (!IS_FIBRE_OPTIC(element))
409 static int steps_grid_auto = 0;
411 if (game_mm.num_cycle == 0) /* initialize cycle steps for grids */
412 steps_grid_auto = RND(16) * (RND(2) ? -1 : +1);
414 if (IS_GRID_STEEL_AUTO(element) ||
415 IS_GRID_WOOD_AUTO(element))
416 game_mm.cycle[game_mm.num_cycle].steps = steps_grid_auto;
418 game_mm.cycle[game_mm.num_cycle].steps = RND(16) * (RND(2) ? -1 : +1);
420 game_mm.cycle[game_mm.num_cycle].x = x;
421 game_mm.cycle[game_mm.num_cycle].y = y;
425 if (IS_BEAMER(element) || IS_FIBRE_OPTIC(element))
427 int beamer_nr = BEAMER_NR(element);
428 int nr = laser.beamer[beamer_nr][0].num;
430 laser.beamer[beamer_nr][nr].x = x;
431 laser.beamer[beamer_nr][nr].y = y;
432 laser.beamer[beamer_nr][nr].num = 1;
435 else if (IS_PACMAN(element))
439 else if (IS_MCDUFFIN(element) || IS_LASER(element))
441 laser.start_edge.x = x;
442 laser.start_edge.y = y;
443 laser.start_angle = get_element_angle(element);
450 static void InitCycleElements_RotateSingleStep()
454 if (game_mm.num_cycle == 0) /* no elements to cycle */
457 for (i = 0; i < game_mm.num_cycle; i++)
459 int x = game_mm.cycle[i].x;
460 int y = game_mm.cycle[i].y;
461 int step = SIGN(game_mm.cycle[i].steps);
462 int last_element = Feld[x][y];
463 int next_element = get_rotated_element(last_element, step);
465 if (!game_mm.cycle[i].steps)
468 Feld[x][y] = next_element;
471 game_mm.cycle[i].steps -= step;
475 static void InitLaser()
477 int start_element = Feld[laser.start_edge.x][laser.start_edge.y];
478 int step = (IS_LASER(start_element) ? 4 : 0);
480 LX = laser.start_edge.x * TILEX;
481 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
484 LX += (laser.start_angle == ANG_RAY_RIGHT ? 28 + step : 0 - step);
486 LY = laser.start_edge.y * TILEY;
487 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
488 LY += (laser.start_angle == ANG_RAY_DOWN ? 28 + step : 0 - step);
492 XS = 2 * Step[laser.start_angle].x;
493 YS = 2 * Step[laser.start_angle].y;
495 laser.current_angle = laser.start_angle;
497 laser.num_damages = 0;
499 laser.num_beamers = 0;
500 laser.beamer_edge[0] = 0;
502 laser.dest_element = EL_EMPTY;
505 AddLaserEdge(LX, LY); /* set laser starting edge */
507 pen_ray = GetPixelFromRGB(window,
508 native_mm_level.laser_red * 0xFF,
509 native_mm_level.laser_green * 0xFF,
510 native_mm_level.laser_blue * 0xFF);
513 void InitGameEngine_MM()
519 /* initialize laser bitmap to current playfield (screen) size */
520 ReCreateBitmap(&laser_bitmap, drawto->width, drawto->height);
521 ClearRectangle(laser_bitmap, 0, 0, drawto->width, drawto->height);
525 /* set global game control values */
526 game_mm.num_cycle = 0;
527 game_mm.num_pacman = 0;
530 game_mm.energy_left = 0; // later set to "native_mm_level.time"
531 game_mm.kettles_still_needed =
532 (native_mm_level.auto_count_kettles ? 0 : native_mm_level.kettles_needed);
533 game_mm.lights_still_needed = 0;
534 game_mm.num_keys = 0;
536 game_mm.level_solved = FALSE;
537 game_mm.game_over = FALSE;
538 game_mm.game_over_cause = 0;
540 game_mm.laser_overload_value = 0;
542 /* set global laser control values (must be set before "InitLaser()") */
543 laser.start_edge.x = 0;
544 laser.start_edge.y = 0;
545 laser.start_angle = 0;
547 for (i = 0; i < MAX_NUM_BEAMERS; i++)
548 laser.beamer[i][0].num = laser.beamer[i][1].num = 0;
550 laser.overloaded = FALSE;
551 laser.overload_value = 0;
552 laser.fuse_off = FALSE;
553 laser.fuse_x = laser.fuse_y = -1;
555 laser.dest_element = EL_EMPTY;
560 for (x = 0; x < lev_fieldx; x++)
562 for (y = 0; y < lev_fieldy; y++)
564 Feld[x][y] = Ur[x][y];
565 Hit[x][y] = Box[x][y] = 0;
567 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
568 Store[x][y] = Store2[x][y] = 0;
572 InitField(x, y, TRUE);
577 CloseDoor(DOOR_CLOSE_1);
583 void InitGameActions_MM()
585 int num_init_game_frames = INIT_GAME_ACTIONS_DELAY;
586 int cycle_steps_done = 0;
592 /* copy default game door content to main double buffer */
593 BlitBitmap(pix[PIX_DOOR], drawto,
594 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
598 DrawText(DX_LEVEL, DY_LEVEL,
599 int2str(level_nr, 2), FONT_TEXT_2);
600 DrawText(DX_KETTLES, DY_KETTLES,
601 int2str(game_mm.kettles_still_needed, 3), FONT_TEXT_2);
602 DrawText(DX_SCORE, DY_SCORE,
603 int2str(game_mm.score, 4), FONT_TEXT_2);
612 /* copy actual game door content to door double buffer for OpenDoor() */
613 BlitBitmap(drawto, pix[PIX_DB_DOOR],
614 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
618 OpenDoor(DOOR_OPEN_ALL);
621 for (i = 0; i <= num_init_game_frames; i++)
623 if (i == num_init_game_frames)
624 StopSound_MM(SND_MM_GAME_LEVELTIME_CHARGING);
625 else if (setup.sound_loops)
626 PlaySoundLoop_MM(SND_MM_GAME_LEVELTIME_CHARGING);
628 PlaySound_MM(SND_MM_GAME_LEVELTIME_CHARGING);
630 game_mm.energy_left = native_mm_level.time * i / num_init_game_frames;
632 UpdateAndDisplayGameControlValues();
634 while (cycle_steps_done < NUM_INIT_CYCLE_STEPS * i / num_init_game_frames)
636 InitCycleElements_RotateSingleStep();
646 if (setup.quick_doors)
652 if (setup.sound_music && num_bg_loops)
653 PlayMusic(level_nr % num_bg_loops);
659 void AddLaserEdge(int lx, int ly)
661 if (lx < -2 || ly < -2 || lx >= SXSIZE + 2 || ly >= SYSIZE + 2)
663 Error(ERR_WARN, "AddLaserEdge: out of bounds: %d, %d", lx, ly);
668 laser.edge[laser.num_edges].x = SX + 2 + lx;
669 laser.edge[laser.num_edges].y = SY + 2 + ly;
675 void AddDamagedField(int ex, int ey)
677 laser.damage[laser.num_damages].is_mirror = FALSE;
678 laser.damage[laser.num_damages].angle = laser.current_angle;
679 laser.damage[laser.num_damages].edge = laser.num_edges;
680 laser.damage[laser.num_damages].x = ex;
681 laser.damage[laser.num_damages].y = ey;
691 int last_x = laser.edge[laser.num_edges - 1].x - SX - 2;
692 int last_y = laser.edge[laser.num_edges - 1].y - SY - 2;
694 return ((x - last_x) * XS < 0 || (y - last_y) * YS < 0);
700 static int getMaskFromElement(int element)
702 if (IS_GRID(element))
703 return IMG_MM_MASK_GRID_1 + get_element_phase(element);
704 else if (IS_MCDUFFIN(element))
705 return IMG_MM_MASK_MCDUFFIN_RIGHT + get_element_phase(element);
706 else if (IS_RECTANGLE(element) || IS_DF_GRID(element))
707 return IMG_MM_MASK_RECTANGLE;
709 return IMG_MM_MASK_CIRCLE;
717 printf("ScanPixel: start scanning at (%d, %d) [%d, %d] [%d, %d]\n",
718 LX, LY, LX / TILEX, LY / TILEY, LX % TILEX, LY % TILEY);
721 /* follow laser beam until it hits something (at least the screen border) */
722 while (hit_mask == HIT_MASK_NO_HIT)
728 if (SX + LX < REAL_SX || SX + LX >= REAL_SX + FULL_SXSIZE ||
729 SY + LY < REAL_SY || SY + LY >= REAL_SY + FULL_SYSIZE)
731 printf("ScanPixel: touched screen border!\n");
737 for (i = 0; i < 4; i++)
739 int px = LX + (i % 2) * 2;
740 int py = LY + (i / 2) * 2;
743 int lx = (px + TILEX) / TILEX - 1; /* ...+TILEX...-1 to get correct */
744 int ly = (py + TILEY) / TILEY - 1; /* negative values! */
747 if (IN_LEV_FIELD(lx, ly))
749 int element = Feld[lx][ly];
751 if (element == EL_EMPTY || element == EL_EXPLODING_TRANSP)
755 else if (IS_WALL(element) || IS_WALL_CHANGING(element))
757 int pos = dy / MINI_TILEY * 2 + dx / MINI_TILEX;
759 pixel = ((element & (1 << pos)) ? 1 : 0);
763 int pos = getMaskFromElement(element) - IMG_MM_MASK_MCDUFFIN_RIGHT;
765 pixel = (mm_masks[pos][dy / 2][dx / 2] == 'X' ? 1 : 0);
770 pixel = (SX + px < REAL_SX || SX + px >= REAL_SX + FULL_SXSIZE ||
771 SY + py < REAL_SY || SY + py >= REAL_SY + FULL_SYSIZE);
774 if ((Sign[laser.current_angle] & (1 << i)) && pixel)
775 hit_mask |= (1 << i);
778 if (hit_mask == HIT_MASK_NO_HIT)
780 /* hit nothing -- go on with another step */
792 int end = 0, rf = laser.num_edges;
794 /* do not scan laser again after the game was lost for whatever reason */
795 if (game_mm.game_over)
798 laser.overloaded = FALSE;
799 laser.stops_inside_element = FALSE;
801 DrawLaser(0, DL_LASER_ENABLED);
804 printf("Start scanning with LX == %d, LY == %d, XS == %d, YS == %d\n",
812 if (laser.num_edges > MAX_LASER_LEN || laser.num_damages > MAX_LASER_LEN)
815 laser.overloaded = TRUE;
820 hit_mask = ScanPixel();
823 printf("Hit something at LX == %d, LY == %d, XS == %d, YS == %d\n",
827 /* hit something -- check out what it was */
828 ELX = (LX + XS) / TILEX;
829 ELY = (LY + YS) / TILEY;
832 printf("hit_mask (1) == '%x' (%d, %d) (%d, %d)\n",
833 hit_mask, LX, LY, ELX, ELY);
836 if (!IN_LEV_FIELD(ELX, ELY) || !IN_PIX_FIELD(LX, LY))
839 laser.dest_element = element;
844 if (hit_mask == (HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT))
846 /* we have hit the top-right and bottom-left element --
847 choose the bottom-left one */
848 /* !!! THIS CAN BE DONE MORE INTELLIGENTLY, FOR EXAMPLE, IF ONE
849 ELEMENT WAS STEEL AND THE OTHER ONE WAS ICE => ALWAYS CHOOSE
850 THE ICE AND MELT IT AWAY INSTEAD OF OVERLOADING LASER !!! */
851 ELX = (LX - 2) / TILEX;
852 ELY = (LY + 2) / TILEY;
855 if (hit_mask == (HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT))
857 /* we have hit the top-left and bottom-right element --
858 choose the top-left one */
859 /* !!! SEE ABOVE !!! */
860 ELX = (LX - 2) / TILEX;
861 ELY = (LY - 2) / TILEY;
865 printf("hit_mask (2) == '%x' (%d, %d) (%d, %d)\n",
866 hit_mask, LX, LY, ELX, ELY);
869 element = Feld[ELX][ELY];
870 laser.dest_element = element;
873 printf("Hit element %d at (%d, %d) [%d, %d] [%d, %d] [%d]\n",
876 LX % TILEX, LY % TILEY,
881 if (!IN_LEV_FIELD(ELX, ELY))
882 printf("WARNING! (1) %d, %d (%d)\n", ELX, ELY, element);
885 if (element == EL_EMPTY)
887 if (!HitOnlyAnEdge(element, hit_mask))
890 else if (element == EL_FUSE_ON)
892 if (HitPolarizer(element, hit_mask))
895 else if (IS_GRID(element) || IS_DF_GRID(element))
897 if (HitPolarizer(element, hit_mask))
900 else if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD ||
901 element == EL_GATE_STONE || element == EL_GATE_WOOD)
903 if (HitBlock(element, hit_mask))
910 else if (IS_MCDUFFIN(element))
912 if (HitLaserSource(element, hit_mask))
915 else if ((element >= EL_EXIT_CLOSED && element <= EL_EXIT_OPEN) ||
916 IS_RECEIVER(element))
918 if (HitLaserDestination(element, hit_mask))
921 else if (IS_WALL(element))
923 if (IS_WALL_STEEL(element) || IS_DF_WALL_STEEL(element))
925 if (HitReflectingWalls(element, hit_mask))
930 if (HitAbsorbingWalls(element, hit_mask))
936 if (HitElement(element, hit_mask))
941 DrawLaser(rf - 1, DL_LASER_ENABLED);
942 rf = laser.num_edges;
946 if (laser.dest_element != Feld[ELX][ELY])
948 printf("ALARM: laser.dest_element == %d, Feld[ELX][ELY] == %d\n",
949 laser.dest_element, Feld[ELX][ELY]);
953 if (!end && !laser.stops_inside_element && !StepBehind())
956 printf("ScanLaser: Go one step back\n");
962 AddLaserEdge(LX, LY);
966 DrawLaser(rf - 1, DL_LASER_ENABLED);
968 Ct = CT = FrameCounter;
971 if (!IN_LEV_FIELD(ELX, ELY))
972 printf("WARNING! (2) %d, %d\n", ELX, ELY);
976 void DrawLaserExt(int start_edge, int num_edges, int mode)
982 printf("DrawLaserExt: start_edge, num_edges, mode == %d, %d, %d\n",
983 start_edge, num_edges, mode);
988 Error(ERR_WARN, "DrawLaserExt: start_edge < 0");
995 Error(ERR_WARN, "DrawLaserExt: num_edges < 0");
1001 if (mode == DL_LASER_DISABLED)
1003 printf("DrawLaser: Delete laser from edge %d\n", start_edge);
1007 /* now draw the laser to the backbuffer and (if enabled) to the screen */
1008 DrawLaserLines(&laser.edge[start_edge], num_edges, mode);
1010 redraw_mask |= REDRAW_FIELD;
1012 if (mode == DL_LASER_ENABLED)
1015 /* after the laser was deleted, the "damaged" graphics must be restored */
1016 if (laser.num_damages)
1018 int damage_start = 0;
1021 /* determine the starting edge, from which graphics need to be restored */
1024 for (i = 0; i < laser.num_damages; i++)
1026 if (laser.damage[i].edge == start_edge + 1)
1035 /* restore graphics from this starting edge to the end of damage list */
1036 for (i = damage_start; i < laser.num_damages; i++)
1038 int lx = laser.damage[i].x;
1039 int ly = laser.damage[i].y;
1040 int element = Feld[lx][ly];
1042 if (Hit[lx][ly] == laser.damage[i].edge)
1043 if (!((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1046 if (Box[lx][ly] == laser.damage[i].edge)
1049 if (IS_DRAWABLE(element))
1050 DrawField_MM(lx, ly);
1053 elx = laser.damage[damage_start].x;
1054 ely = laser.damage[damage_start].y;
1055 element = Feld[elx][ely];
1058 if (IS_BEAMER(element))
1062 for (i = 0; i < laser.num_beamers; i++)
1063 printf("-> %d\n", laser.beamer_edge[i]);
1064 printf("DrawLaserExt: IS_BEAMER: [%d]: Hit[%d][%d] == %d [%d]\n",
1065 mode, elx, ely, Hit[elx][ely], start_edge);
1066 printf("DrawLaserExt: IS_BEAMER: %d / %d\n",
1067 get_element_angle(element), laser.damage[damage_start].angle);
1071 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1072 laser.num_beamers > 0 &&
1073 start_edge == laser.beamer_edge[laser.num_beamers - 1])
1075 /* element is outgoing beamer */
1076 laser.num_damages = damage_start + 1;
1078 if (IS_BEAMER(element))
1079 laser.current_angle = get_element_angle(element);
1083 /* element is incoming beamer or other element */
1084 laser.num_damages = damage_start;
1085 laser.current_angle = laser.damage[laser.num_damages].angle;
1090 /* no damages but McDuffin himself (who needs to be redrawn anyway) */
1092 elx = laser.start_edge.x;
1093 ely = laser.start_edge.y;
1094 element = Feld[elx][ely];
1097 laser.num_edges = start_edge + 1;
1098 if (start_edge == 0)
1099 laser.current_angle = laser.start_angle;
1101 LX = laser.edge[start_edge].x - (SX + 2);
1102 LY = laser.edge[start_edge].y - (SY + 2);
1103 XS = 2 * Step[laser.current_angle].x;
1104 YS = 2 * Step[laser.current_angle].y;
1107 printf("DrawLaser: Set (LX, LY) to (%d, %d) [%d]\n",
1113 if (IS_BEAMER(element) ||
1114 IS_FIBRE_OPTIC(element) ||
1115 IS_PACMAN(element) ||
1116 IS_POLAR(element) ||
1117 IS_POLAR_CROSS(element) ||
1118 element == EL_FUSE_ON)
1123 printf("element == %d\n", element);
1126 if (IS_22_5_ANGLE(laser.current_angle)) /* neither 90° nor 45° angle */
1127 step_size = ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) ? 4 : 3);
1131 if (IS_POLAR(element) || IS_POLAR_CROSS(element) ||
1132 ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1133 (laser.num_beamers == 0 ||
1134 start_edge != laser.beamer_edge[laser.num_beamers - 1])))
1136 /* element is incoming beamer or other element */
1137 step_size = -step_size;
1142 if (IS_BEAMER(element))
1144 printf("start_edge == %d, laser.beamer_edge == %d\n",
1145 start_edge, laser.beamer_edge);
1149 LX += step_size * XS;
1150 LY += step_size * YS;
1152 else if (element != EL_EMPTY)
1161 printf("DrawLaser: Finally: (LX, LY) to (%d, %d) [%d]\n",
1166 void DrawLaser(int start_edge, int mode)
1168 if (laser.num_edges - start_edge < 0)
1170 Error(ERR_WARN, "DrawLaser: laser.num_edges - start_edge < 0");
1175 /* check if laser is interrupted by beamer element */
1176 if (laser.num_beamers > 0 &&
1177 start_edge < laser.beamer_edge[laser.num_beamers - 1])
1179 if (mode == DL_LASER_ENABLED)
1182 int tmp_start_edge = start_edge;
1184 /* draw laser segments forward from the start to the last beamer */
1185 for (i = 0; i < laser.num_beamers; i++)
1187 int tmp_num_edges = laser.beamer_edge[i] - tmp_start_edge;
1189 if (tmp_num_edges <= 0)
1193 printf("DrawLaser: DL_LASER_ENABLED: i==%d: %d, %d\n",
1194 i, laser.beamer_edge[i], tmp_start_edge);
1197 DrawLaserExt(tmp_start_edge, tmp_num_edges, DL_LASER_ENABLED);
1199 tmp_start_edge = laser.beamer_edge[i];
1202 /* draw last segment from last beamer to the end */
1203 DrawLaserExt(tmp_start_edge, laser.num_edges - tmp_start_edge,
1209 int last_num_edges = laser.num_edges;
1210 int num_beamers = laser.num_beamers;
1212 /* delete laser segments backward from the end to the first beamer */
1213 for (i = num_beamers-1; i >= 0; i--)
1215 int tmp_num_edges = last_num_edges - laser.beamer_edge[i];
1217 if (laser.beamer_edge[i] - start_edge <= 0)
1220 DrawLaserExt(laser.beamer_edge[i], tmp_num_edges, DL_LASER_DISABLED);
1222 last_num_edges = laser.beamer_edge[i];
1223 laser.num_beamers--;
1227 if (last_num_edges - start_edge <= 0)
1228 printf("DrawLaser: DL_LASER_DISABLED: %d, %d\n",
1229 last_num_edges, start_edge);
1232 /* delete first segment from start to the first beamer */
1233 DrawLaserExt(start_edge, last_num_edges - start_edge, DL_LASER_DISABLED);
1238 DrawLaserExt(start_edge, laser.num_edges - start_edge, mode);
1242 boolean HitElement(int element, int hit_mask)
1244 if (HitOnlyAnEdge(element, hit_mask))
1247 if (IS_MOVING(ELX, ELY) || IS_BLOCKED(ELX, ELY))
1248 element = MovingOrBlocked2Element_MM(ELX, ELY);
1251 printf("HitElement (1): element == %d\n", element);
1255 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1256 printf("HitElement (%d): EXACT MATCH @ (%d, %d)\n", element, ELX, ELY);
1258 printf("HitElement (%d): FUZZY MATCH @ (%d, %d)\n", element, ELX, ELY);
1261 AddDamagedField(ELX, ELY);
1263 /* this is more precise: check if laser would go through the center */
1264 if ((ELX * TILEX + 14 - LX) * YS != (ELY * TILEY + 14 - LY) * XS)
1266 /* skip the whole element before continuing the scan */
1272 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1274 if (LX/TILEX > ELX || LY/TILEY > ELY)
1276 /* skipping scan positions to the right and down skips one scan
1277 position too much, because this is only the top left scan position
1278 of totally four scan positions (plus one to the right, one to the
1279 bottom and one to the bottom right) */
1289 printf("HitElement (2): element == %d\n", element);
1292 if (LX + 5 * XS < 0 ||
1302 printf("HitElement (3): element == %d\n", element);
1305 if (IS_POLAR(element) &&
1306 ((element - EL_POLAR_START) % 2 ||
1307 (element - EL_POLAR_START) / 2 != laser.current_angle % 8))
1309 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1311 laser.num_damages--;
1316 if (IS_POLAR_CROSS(element) &&
1317 (element - EL_POLAR_CROSS_START) != laser.current_angle % 4)
1319 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1321 laser.num_damages--;
1326 if (!IS_BEAMER(element) &&
1327 !IS_FIBRE_OPTIC(element) &&
1328 !IS_GRID_WOOD(element) &&
1329 element != EL_FUEL_EMPTY)
1332 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1333 printf("EXACT MATCH @ (%d, %d)\n", ELX, ELY);
1335 printf("FUZZY MATCH @ (%d, %d)\n", ELX, ELY);
1338 LX = ELX * TILEX + 14;
1339 LY = ELY * TILEY + 14;
1341 AddLaserEdge(LX, LY);
1344 if (IS_MIRROR(element) ||
1345 IS_MIRROR_FIXED(element) ||
1346 IS_POLAR(element) ||
1347 IS_POLAR_CROSS(element) ||
1348 IS_DF_MIRROR(element) ||
1349 IS_DF_MIRROR_AUTO(element) ||
1350 element == EL_PRISM ||
1351 element == EL_REFRACTOR)
1353 int current_angle = laser.current_angle;
1356 laser.num_damages--;
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 (IS_MIRROR(element) ||
1366 IS_MIRROR_FIXED(element) ||
1367 IS_DF_MIRROR(element) ||
1368 IS_DF_MIRROR_AUTO(element))
1369 laser.current_angle = get_mirrored_angle(laser.current_angle,
1370 get_element_angle(element));
1372 if (element == EL_PRISM || element == EL_REFRACTOR)
1373 laser.current_angle = RND(16);
1375 XS = 2 * Step[laser.current_angle].x;
1376 YS = 2 * Step[laser.current_angle].y;
1378 if (!IS_22_5_ANGLE(laser.current_angle)) /* 90° or 45° angle */
1383 LX += step_size * XS;
1384 LY += step_size * YS;
1387 /* draw sparkles on mirror */
1388 if ((IS_MIRROR(element) || IS_MIRROR_FIXED(element)) &&
1389 current_angle != laser.current_angle)
1391 MoveSprite(vp, &Pfeil[2], 4 + 16 * ELX, 5 + 16 * ELY + 1);
1395 if ((!IS_POLAR(element) && !IS_POLAR_CROSS(element)) &&
1396 current_angle != laser.current_angle)
1397 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1400 (get_opposite_angle(laser.current_angle) ==
1401 laser.damage[laser.num_damages - 1].angle ? TRUE : FALSE);
1403 return (laser.overloaded ? TRUE : FALSE);
1406 if (element == EL_FUEL_FULL)
1408 laser.stops_inside_element = TRUE;
1413 if (element == EL_BOMB || element == EL_MINE)
1415 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1417 if (element == EL_MINE)
1418 laser.overloaded = TRUE;
1421 if (element == EL_KETTLE ||
1422 element == EL_CELL ||
1423 element == EL_KEY ||
1424 element == EL_LIGHTBALL ||
1425 element == EL_PACMAN ||
1428 if (!IS_PACMAN(element))
1431 if (element == EL_PACMAN)
1434 if (element == EL_KETTLE || element == EL_CELL)
1436 if (game_mm.kettles_still_needed > 0)
1437 game_mm.kettles_still_needed--;
1441 if (game_mm.kettles_still_needed == 0)
1443 int exit_element = (element == EL_KETTLE ? EL_EXIT_OPEN : EL_RECEIVER);
1445 static int xy[4][2] =
1453 PlayLevelSound_MM(ELX, ELY, exit_element, MM_ACTION_OPENING);
1455 for (y = 0; y < lev_fieldy; y++)
1457 for (x = 0; x < lev_fieldx; x++)
1459 /* initiate opening animation of exit door */
1460 if (Feld[x][y] == EL_EXIT_CLOSED)
1461 Feld[x][y] = EL_EXIT_OPENING;
1463 /* remove field that blocks receiver */
1464 if (IS_RECEIVER(Feld[x][y]))
1466 int phase = Feld[x][y] - EL_RECEIVER_START;
1467 int blocking_x, blocking_y;
1469 blocking_x = x + xy[phase][0];
1470 blocking_y = y + xy[phase][1];
1472 if (IN_LEV_FIELD(blocking_x, blocking_y))
1474 Feld[blocking_x][blocking_y] = EL_EMPTY;
1476 DrawField_MM(blocking_x, blocking_y);
1482 DrawLaser(0, DL_LASER_ENABLED);
1485 else if (element == EL_KEY)
1489 else if (element == EL_LIGHTBALL)
1493 else if (IS_PACMAN(element))
1495 DeletePacMan(ELX, ELY);
1502 if (element == EL_LIGHTBULB_OFF || element == EL_LIGHTBULB_ON)
1504 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1506 DrawLaser(0, DL_LASER_ENABLED);
1508 if (Feld[ELX][ELY] == EL_LIGHTBULB_OFF)
1510 Feld[ELX][ELY] = EL_LIGHTBULB_ON;
1511 game_mm.lights_still_needed--;
1515 Feld[ELX][ELY] = EL_LIGHTBULB_OFF;
1516 game_mm.lights_still_needed++;
1519 DrawField_MM(ELX, ELY);
1520 DrawLaser(0, DL_LASER_ENABLED);
1525 laser.stops_inside_element = TRUE;
1531 printf("HitElement (4): element == %d\n", element);
1534 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1535 laser.num_beamers < MAX_NUM_BEAMERS &&
1536 laser.beamer[BEAMER_NR(element)][1].num)
1538 int beamer_angle = get_element_angle(element);
1539 int beamer_nr = BEAMER_NR(element);
1543 printf("HitElement (BEAMER): element == %d\n", element);
1546 laser.num_damages--;
1548 if (IS_FIBRE_OPTIC(element) ||
1549 laser.current_angle == get_opposite_angle(beamer_angle))
1553 LX = ELX * TILEX + 14;
1554 LY = ELY * TILEY + 14;
1556 AddLaserEdge(LX, LY);
1557 AddDamagedField(ELX, ELY);
1559 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1562 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1564 pos = (ELX == laser.beamer[beamer_nr][0].x &&
1565 ELY == laser.beamer[beamer_nr][0].y ? 1 : 0);
1566 ELX = laser.beamer[beamer_nr][pos].x;
1567 ELY = laser.beamer[beamer_nr][pos].y;
1568 LX = ELX * TILEX + 14;
1569 LY = ELY * TILEY + 14;
1571 if (IS_BEAMER(element))
1573 laser.current_angle = get_element_angle(Feld[ELX][ELY]);
1574 XS = 2 * Step[laser.current_angle].x;
1575 YS = 2 * Step[laser.current_angle].y;
1578 laser.beamer_edge[laser.num_beamers] = laser.num_edges;
1580 AddLaserEdge(LX, LY);
1581 AddDamagedField(ELX, ELY);
1583 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1586 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1588 if (laser.current_angle == (laser.current_angle >> 1) << 1)
1593 LX += step_size * XS;
1594 LY += step_size * YS;
1596 laser.num_beamers++;
1605 boolean HitOnlyAnEdge(int element, int hit_mask)
1607 /* check if the laser hit only the edge of an element and, if so, go on */
1610 printf("LX, LY, hit_mask == %d, %d, %d\n", LX, LY, hit_mask);
1613 if ((hit_mask == HIT_MASK_TOPLEFT ||
1614 hit_mask == HIT_MASK_TOPRIGHT ||
1615 hit_mask == HIT_MASK_BOTTOMLEFT ||
1616 hit_mask == HIT_MASK_BOTTOMRIGHT) &&
1617 laser.current_angle % 4) /* angle is not 90° */
1621 if (hit_mask == HIT_MASK_TOPLEFT)
1626 else if (hit_mask == HIT_MASK_TOPRIGHT)
1631 else if (hit_mask == HIT_MASK_BOTTOMLEFT)
1636 else /* (hit_mask == HIT_MASK_BOTTOMRIGHT) */
1642 AddDamagedField((LX + 2 * dx) / TILEX, (LY + 2 * dy) / TILEY);
1648 printf("[HitOnlyAnEdge() == TRUE]\n");
1655 printf("[HitOnlyAnEdge() == FALSE]\n");
1661 boolean HitPolarizer(int element, int hit_mask)
1663 if (HitOnlyAnEdge(element, hit_mask))
1666 if (IS_DF_GRID(element))
1668 int grid_angle = get_element_angle(element);
1671 printf("HitPolarizer: angle: grid == %d, laser == %d\n",
1672 grid_angle, laser.current_angle);
1675 AddLaserEdge(LX, LY);
1676 AddDamagedField(ELX, ELY);
1679 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1681 if (laser.current_angle == grid_angle ||
1682 laser.current_angle == get_opposite_angle(grid_angle))
1684 /* skip the whole element before continuing the scan */
1690 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1692 if (LX/TILEX > ELX || LY/TILEY > ELY)
1694 /* skipping scan positions to the right and down skips one scan
1695 position too much, because this is only the top left scan position
1696 of totally four scan positions (plus one to the right, one to the
1697 bottom and one to the bottom right) */
1703 AddLaserEdge(LX, LY);
1709 printf("HitPolarizer: LX, LY == %d, %d [%d, %d] [%d, %d]\n",
1711 LX / TILEX, LY / TILEY,
1712 LX % TILEX, LY % TILEY);
1717 else if (IS_GRID_STEEL_FIXED(element) || IS_GRID_STEEL_AUTO(element))
1719 return HitReflectingWalls(element, hit_mask);
1723 return HitAbsorbingWalls(element, hit_mask);
1726 else if (IS_GRID_STEEL(element))
1728 return HitReflectingWalls(element, hit_mask);
1730 else /* IS_GRID_WOOD */
1732 return HitAbsorbingWalls(element, hit_mask);
1738 boolean HitBlock(int element, int hit_mask)
1740 boolean check = FALSE;
1742 if ((element == EL_GATE_STONE || element == EL_GATE_WOOD) &&
1743 game_mm.num_keys == 0)
1746 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1749 int ex = ELX * TILEX + 14;
1750 int ey = ELY * TILEY + 14;
1754 for (i = 1; i < 32; i++)
1759 if ((x == ex || x == ex + 1) && (y == ey || y == ey + 1))
1764 if (check && (element == EL_BLOCK_WOOD || element == EL_GATE_WOOD))
1765 return HitAbsorbingWalls(element, hit_mask);
1769 AddLaserEdge(LX - XS, LY - YS);
1770 AddDamagedField(ELX, ELY);
1773 Box[ELX][ELY] = laser.num_edges;
1775 return HitReflectingWalls(element, hit_mask);
1778 if (element == EL_GATE_STONE || element == EL_GATE_WOOD)
1780 int xs = XS / 2, ys = YS / 2;
1781 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1782 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1784 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1785 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1787 laser.overloaded = (element == EL_GATE_STONE);
1792 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1793 (hit_mask == HIT_MASK_TOP ||
1794 hit_mask == HIT_MASK_LEFT ||
1795 hit_mask == HIT_MASK_RIGHT ||
1796 hit_mask == HIT_MASK_BOTTOM))
1797 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1798 hit_mask == HIT_MASK_BOTTOM),
1799 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1800 hit_mask == HIT_MASK_RIGHT));
1801 AddLaserEdge(LX, LY);
1807 if (element == EL_GATE_STONE && Box[ELX][ELY])
1809 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
1821 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1823 int xs = XS / 2, ys = YS / 2;
1824 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1825 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1827 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1828 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1830 laser.overloaded = (element == EL_BLOCK_STONE);
1835 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1836 (hit_mask == HIT_MASK_TOP ||
1837 hit_mask == HIT_MASK_LEFT ||
1838 hit_mask == HIT_MASK_RIGHT ||
1839 hit_mask == HIT_MASK_BOTTOM))
1840 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1841 hit_mask == HIT_MASK_BOTTOM),
1842 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1843 hit_mask == HIT_MASK_RIGHT));
1844 AddDamagedField(ELX, ELY);
1846 LX = ELX * TILEX + 14;
1847 LY = ELY * TILEY + 14;
1849 AddLaserEdge(LX, LY);
1851 laser.stops_inside_element = TRUE;
1859 boolean HitLaserSource(int element, int hit_mask)
1861 if (HitOnlyAnEdge(element, hit_mask))
1864 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1866 laser.overloaded = TRUE;
1871 boolean HitLaserDestination(int element, int hit_mask)
1873 if (HitOnlyAnEdge(element, hit_mask))
1876 if (element != EL_EXIT_OPEN &&
1877 !(IS_RECEIVER(element) &&
1878 game_mm.kettles_still_needed == 0 &&
1879 laser.current_angle == get_opposite_angle(get_element_angle(element))))
1881 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1886 if (IS_RECEIVER(element) ||
1887 (IS_22_5_ANGLE(laser.current_angle) &&
1888 (ELX != (LX + 6 * XS) / TILEX ||
1889 ELY != (LY + 6 * YS) / TILEY ||
1898 LX = ELX * TILEX + 14;
1899 LY = ELY * TILEY + 14;
1901 laser.stops_inside_element = TRUE;
1904 AddLaserEdge(LX, LY);
1905 AddDamagedField(ELX, ELY);
1907 if (game_mm.lights_still_needed == 0)
1908 game_mm.level_solved = TRUE;
1913 boolean HitReflectingWalls(int element, int hit_mask)
1915 /* check if laser hits side of a wall with an angle that is not 90° */
1916 if (!IS_90_ANGLE(laser.current_angle) && (hit_mask == HIT_MASK_TOP ||
1917 hit_mask == HIT_MASK_LEFT ||
1918 hit_mask == HIT_MASK_RIGHT ||
1919 hit_mask == HIT_MASK_BOTTOM))
1921 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1926 if (!IS_DF_GRID(element))
1927 AddLaserEdge(LX, LY);
1929 /* check if laser hits wall with an angle of 45° */
1930 if (!IS_22_5_ANGLE(laser.current_angle))
1932 if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1935 laser.current_angle = get_mirrored_angle(laser.current_angle,
1938 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1941 laser.current_angle = get_mirrored_angle(laser.current_angle,
1945 AddLaserEdge(LX, LY);
1947 XS = 2 * Step[laser.current_angle].x;
1948 YS = 2 * Step[laser.current_angle].y;
1952 else if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1954 laser.current_angle = get_mirrored_angle(laser.current_angle,
1959 if (!IS_DF_GRID(element))
1960 AddLaserEdge(LX, LY);
1965 if (!IS_DF_GRID(element))
1966 AddLaserEdge(LX, LY + YS / 2);
1969 if (!IS_DF_GRID(element))
1970 AddLaserEdge(LX, LY);
1973 YS = 2 * Step[laser.current_angle].y;
1977 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1979 laser.current_angle = get_mirrored_angle(laser.current_angle,
1984 if (!IS_DF_GRID(element))
1985 AddLaserEdge(LX, LY);
1990 if (!IS_DF_GRID(element))
1991 AddLaserEdge(LX + XS / 2, LY);
1994 if (!IS_DF_GRID(element))
1995 AddLaserEdge(LX, LY);
1998 XS = 2 * Step[laser.current_angle].x;
2004 /* reflection at the edge of reflecting DF style wall */
2005 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
2007 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
2008 hit_mask == HIT_MASK_TOPRIGHT) ||
2009 ((laser.current_angle == 5 || laser.current_angle == 7) &&
2010 hit_mask == HIT_MASK_TOPLEFT) ||
2011 ((laser.current_angle == 9 || laser.current_angle == 11) &&
2012 hit_mask == HIT_MASK_BOTTOMLEFT) ||
2013 ((laser.current_angle == 13 || laser.current_angle == 15) &&
2014 hit_mask == HIT_MASK_BOTTOMRIGHT))
2017 (hit_mask == HIT_MASK_TOPRIGHT || hit_mask == HIT_MASK_BOTTOMLEFT ?
2018 ANG_MIRROR_135 : ANG_MIRROR_45);
2020 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
2022 AddDamagedField(ELX, ELY);
2023 AddLaserEdge(LX, LY);
2025 laser.current_angle = get_mirrored_angle(laser.current_angle,
2033 AddLaserEdge(LX, LY);
2039 /* reflection inside an edge of reflecting DF style wall */
2040 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
2042 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
2043 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT)) ||
2044 ((laser.current_angle == 5 || laser.current_angle == 7) &&
2045 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMRIGHT)) ||
2046 ((laser.current_angle == 9 || laser.current_angle == 11) &&
2047 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT)) ||
2048 ((laser.current_angle == 13 || laser.current_angle == 15) &&
2049 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPLEFT)))
2052 (hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT) ||
2053 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT) ?
2054 ANG_MIRROR_135 : ANG_MIRROR_45);
2056 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
2059 AddDamagedField(ELX, ELY);
2062 AddLaserEdge(LX - XS, LY - YS);
2063 AddLaserEdge(LX - XS + (ABS(XS) == 4 ? XS/2 : 0),
2064 LY - YS + (ABS(YS) == 4 ? YS/2 : 0));
2066 laser.current_angle = get_mirrored_angle(laser.current_angle,
2074 AddLaserEdge(LX, LY);
2080 /* check if laser hits DF style wall with an angle of 90° */
2081 if (IS_DF_WALL(element) && IS_90_ANGLE(laser.current_angle))
2083 if ((IS_HORIZ_ANGLE(laser.current_angle) &&
2084 (!(hit_mask & HIT_MASK_TOP) || !(hit_mask & HIT_MASK_BOTTOM))) ||
2085 (IS_VERT_ANGLE(laser.current_angle) &&
2086 (!(hit_mask & HIT_MASK_LEFT) || !(hit_mask & HIT_MASK_RIGHT))))
2088 static int last_LX = 0, last_LY = 0, last_hit_mask = 0;
2090 /* laser at last step touched nothing or the same side of the wall */
2091 if (LX != last_LX || LY != last_LY || hit_mask == last_hit_mask)
2093 AddDamagedField(ELX, ELY);
2100 last_hit_mask = hit_mask;
2107 if (!HitOnlyAnEdge(element, hit_mask))
2109 laser.overloaded = TRUE;
2117 boolean HitAbsorbingWalls(int element, int hit_mask)
2119 if (HitOnlyAnEdge(element, hit_mask))
2123 (hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT))
2125 AddLaserEdge(LX - XS, LY - YS);
2132 (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM))
2134 AddLaserEdge(LX - XS, LY - YS);
2140 if (IS_WALL_WOOD(element) ||
2141 IS_DF_WALL_WOOD(element) ||
2142 IS_GRID_WOOD(element) ||
2143 IS_GRID_WOOD_FIXED(element) ||
2144 IS_GRID_WOOD_AUTO(element) ||
2145 element == EL_FUSE_ON ||
2146 element == EL_BLOCK_WOOD ||
2147 element == EL_GATE_WOOD)
2149 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
2154 if (IS_WALL_ICE(element))
2158 mask = (LX + XS) / MINI_TILEX - ELX * 2 + 1; /* Quadrant (horizontal) */
2159 mask <<= (((LY + YS) / MINI_TILEY - ELY * 2) > 0) * 2; /* || (vertical) */
2161 /* check if laser hits wall with an angle of 90° */
2162 if (IS_90_ANGLE(laser.current_angle))
2163 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
2165 if (mask == 1 || mask == 2 || mask == 4 || mask == 8)
2169 for (i = 0; i < 4; i++)
2171 if (mask == (1 << i) && (XS > 0) == (i % 2) && (YS > 0) == (i / 2))
2172 mask = 15 - (8 >> i);
2173 else if (ABS(XS) == 4 &&
2175 (XS > 0) == (i % 2) &&
2176 (YS < 0) == (i / 2))
2177 mask = 3 + (i / 2) * 9;
2178 else if (ABS(YS) == 4 &&
2180 (XS < 0) == (i % 2) &&
2181 (YS > 0) == (i / 2))
2182 mask = 5 + (i % 2) * 5;
2186 laser.wall_mask = mask;
2188 else if (IS_WALL_AMOEBA(element))
2190 int elx = (LX - 2 * XS) / TILEX;
2191 int ely = (LY - 2 * YS) / TILEY;
2192 int element2 = Feld[elx][ely];
2195 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element2))
2197 laser.dest_element = EL_EMPTY;
2205 mask = (LX - 2 * XS) / 16 - ELX * 2 + 1;
2206 mask <<= ((LY - 2 * YS) / 16 - ELY * 2) * 2;
2208 if (IS_90_ANGLE(laser.current_angle))
2209 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
2211 laser.dest_element = element2 | EL_WALL_AMOEBA;
2213 laser.wall_mask = mask;
2219 void OpenExit(int x, int y)
2223 if (!MovDelay[x][y]) /* next animation frame */
2224 MovDelay[x][y] = 4 * delay;
2226 if (MovDelay[x][y]) /* wait some time before next frame */
2231 phase = MovDelay[x][y] / delay;
2233 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2234 DrawGraphicAnimation_MM(x, y, IMG_MM_EXIT_OPENING, 3 - phase);
2236 if (!MovDelay[x][y])
2238 Feld[x][y] = EL_EXIT_OPEN;
2244 void OpenSurpriseBall(int x, int y)
2248 if (!MovDelay[x][y]) /* next animation frame */
2249 MovDelay[x][y] = 50 * delay;
2251 if (MovDelay[x][y]) /* wait some time before next frame */
2255 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2258 int graphic = el2gfx(Store[x][y]);
2260 int dx = RND(26), dy = RND(26);
2262 getGraphicSource(graphic, 0, &bitmap, &gx, &gy);
2264 BlitBitmap(bitmap, drawto, gx + dx, gy + dy, 6, 6,
2265 SX + x * TILEX + dx, SY + y * TILEY + dy);
2267 MarkTileDirty(x, y);
2270 if (!MovDelay[x][y])
2272 Feld[x][y] = Store[x][y];
2281 void MeltIce(int x, int y)
2286 if (!MovDelay[x][y]) /* next animation frame */
2287 MovDelay[x][y] = frames * delay;
2289 if (MovDelay[x][y]) /* wait some time before next frame */
2292 int wall_mask = Store2[x][y];
2293 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_ICE;
2296 phase = frames - MovDelay[x][y] / delay - 1;
2298 if (!MovDelay[x][y])
2302 Feld[x][y] = real_element & (wall_mask ^ 0xFF);
2303 Store[x][y] = Store2[x][y] = 0;
2305 DrawWalls_MM(x, y, Feld[x][y]);
2307 if (Feld[x][y] == EL_WALL_ICE)
2308 Feld[x][y] = EL_EMPTY;
2310 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
2311 if (laser.damage[i].is_mirror)
2315 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
2317 DrawLaser(0, DL_LASER_DISABLED);
2321 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2323 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2325 laser.redraw = TRUE;
2330 void GrowAmoeba(int x, int y)
2335 if (!MovDelay[x][y]) /* next animation frame */
2336 MovDelay[x][y] = frames * delay;
2338 if (MovDelay[x][y]) /* wait some time before next frame */
2341 int wall_mask = Store2[x][y];
2342 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_AMOEBA;
2345 phase = MovDelay[x][y] / delay;
2347 if (!MovDelay[x][y])
2349 Feld[x][y] = real_element;
2350 Store[x][y] = Store2[x][y] = 0;
2352 DrawWalls_MM(x, y, Feld[x][y]);
2353 DrawLaser(0, DL_LASER_ENABLED);
2355 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2357 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2362 static void Explode_MM(int x, int y, int phase, int mode)
2364 int num_phase = 9, delay = 2;
2365 int last_phase = num_phase * delay;
2366 int half_phase = (num_phase / 2) * delay;
2368 laser.redraw = TRUE;
2370 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2372 int center_element = Feld[x][y];
2374 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2376 /* put moving element to center field (and let it explode there) */
2377 center_element = MovingOrBlocked2Element_MM(x, y);
2378 RemoveMovingField_MM(x, y);
2380 Feld[x][y] = center_element;
2383 if (center_element == EL_BOMB || IS_MCDUFFIN(center_element))
2384 Store[x][y] = center_element;
2386 Store[x][y] = EL_EMPTY;
2388 Store2[x][y] = mode;
2389 Feld[x][y] = EL_EXPLODING_OPAQUE;
2390 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2396 Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
2398 if (phase == half_phase)
2400 Feld[x][y] = EL_EXPLODING_TRANSP;
2402 if (x == ELX && y == ELY)
2406 if (phase == last_phase)
2408 if (Store[x][y] == EL_BOMB)
2410 DrawLaser(0, DL_LASER_DISABLED);
2413 Bang_MM(laser.start_edge.x, laser.start_edge.y);
2414 Store[x][y] = EL_EMPTY;
2416 game_mm.game_over = TRUE;
2417 game_mm.game_over_cause = GAME_OVER_BOMB;
2419 laser.overloaded = FALSE;
2421 else if (IS_MCDUFFIN(Store[x][y]))
2423 Store[x][y] = EL_EMPTY;
2426 Feld[x][y] = Store[x][y];
2427 Store[x][y] = Store2[x][y] = 0;
2428 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2430 InitField(x, y, FALSE);
2433 else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2435 int graphic = IMG_MM_DEFAULT_EXPLODING;
2436 int graphic_phase = (phase / delay - 1);
2440 if (Store2[x][y] == EX_KETTLE)
2442 if (graphic_phase < 3)
2444 graphic = IMG_MM_KETTLE_EXPLODING;
2446 else if (graphic_phase < 5)
2452 graphic = IMG_EMPTY;
2456 else if (Store2[x][y] == EX_SHORT)
2458 if (graphic_phase < 4)
2464 graphic = IMG_EMPTY;
2469 getGraphicSource(graphic, graphic_phase, &bitmap, &src_x, &src_y);
2471 BlitBitmap(bitmap, drawto_field, src_x, src_y, TILEX, TILEY,
2472 FX + x * TILEX, FY + y * TILEY);
2474 MarkTileDirty(x, y);
2478 static void Bang_MM(int x, int y)
2480 int element = Feld[x][y];
2481 int mode = EX_NORMAL;
2484 DrawLaser(0, DL_LASER_ENABLED);
2503 if (IS_PACMAN(element))
2504 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2505 else if (element == EL_BOMB || IS_MCDUFFIN(element))
2506 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2507 else if (element == EL_KEY)
2508 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2510 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2512 Explode_MM(x, y, EX_PHASE_START, mode);
2515 void TurnRound(int x, int y)
2527 { 0, 0 }, { 0, 0 }, { 0, 0 },
2532 int left, right, back;
2536 { MV_DOWN, MV_UP, MV_RIGHT },
2537 { MV_UP, MV_DOWN, MV_LEFT },
2539 { MV_LEFT, MV_RIGHT, MV_DOWN },
2540 { 0,0,0 }, { 0,0,0 }, { 0,0,0 },
2541 { MV_RIGHT, MV_LEFT, MV_UP }
2544 int element = Feld[x][y];
2545 int old_move_dir = MovDir[x][y];
2546 int right_dir = turn[old_move_dir].right;
2547 int back_dir = turn[old_move_dir].back;
2548 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
2549 int right_x = x + right_dx, right_y = y + right_dy;
2551 if (element == EL_PACMAN)
2553 boolean can_turn_right = FALSE;
2555 if (IN_LEV_FIELD(right_x, right_y) &&
2556 IS_EATABLE4PACMAN(Feld[right_x][right_y]))
2557 can_turn_right = TRUE;
2560 MovDir[x][y] = right_dir;
2562 MovDir[x][y] = back_dir;
2568 static void StartMoving_MM(int x, int y)
2570 int element = Feld[x][y];
2575 if (CAN_MOVE(element))
2579 if (MovDelay[x][y]) /* wait some time before next movement */
2587 /* now make next step */
2589 Moving2Blocked_MM(x, y, &newx, &newy); /* get next screen position */
2591 if (element == EL_PACMAN &&
2592 IN_LEV_FIELD(newx, newy) && IS_EATABLE4PACMAN(Feld[newx][newy]) &&
2593 !ObjHit(newx, newy, HIT_POS_CENTER))
2595 Store[newx][newy] = Feld[newx][newy];
2596 Feld[newx][newy] = EL_EMPTY;
2598 DrawField_MM(newx, newy);
2600 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy) ||
2601 ObjHit(newx, newy, HIT_POS_CENTER))
2603 /* object was running against a wall */
2610 InitMovingField_MM(x, y, MovDir[x][y]);
2614 ContinueMoving_MM(x, y);
2617 static void ContinueMoving_MM(int x, int y)
2619 int element = Feld[x][y];
2620 int direction = MovDir[x][y];
2621 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2622 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2623 int horiz_move = (dx!=0);
2624 int newx = x + dx, newy = y + dy;
2625 int step = (horiz_move ? dx : dy) * TILEX / 8;
2627 MovPos[x][y] += step;
2629 if (ABS(MovPos[x][y]) >= TILEX) /* object reached its destination */
2631 Feld[x][y] = EL_EMPTY;
2632 Feld[newx][newy] = element;
2634 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2635 MovDelay[newx][newy] = 0;
2637 if (!CAN_MOVE(element))
2638 MovDir[newx][newy] = 0;
2641 DrawField_MM(newx, newy);
2643 Stop[newx][newy] = TRUE;
2645 if (element == EL_PACMAN)
2647 if (Store[newx][newy] == EL_BOMB)
2648 Bang_MM(newx, newy);
2650 if (IS_WALL_AMOEBA(Store[newx][newy]) &&
2651 (LX + 2 * XS) / TILEX == newx &&
2652 (LY + 2 * YS) / TILEY == newy)
2659 else /* still moving on */
2664 laser.redraw = TRUE;
2667 void ClickElement(int x, int y, int button)
2669 static unsigned int click_delay = 0;
2670 static int click_delay_value = CLICK_DELAY;
2671 static boolean new_button = TRUE;
2674 /* do not rotate objects hit by the laser after the game was solved */
2675 if (game_mm.level_solved && Hit[x][y])
2678 if (button == MB_RELEASED)
2681 click_delay_value = CLICK_DELAY;
2683 /* release eventually hold auto-rotating mirror */
2684 RotateMirror(x, y, MB_RELEASED);
2689 if (!FrameReached(&click_delay, click_delay_value) && !new_button)
2692 if (button == MB_MIDDLEBUTTON) /* middle button has no function */
2695 if (!IN_LEV_FIELD(x, y))
2698 if (Feld[x][y] == EL_EMPTY)
2701 element = Feld[x][y];
2703 if (IS_MIRROR(element) ||
2704 IS_BEAMER(element) ||
2705 IS_POLAR(element) ||
2706 IS_POLAR_CROSS(element) ||
2707 IS_DF_MIRROR(element) ||
2708 IS_DF_MIRROR_AUTO(element))
2710 RotateMirror(x, y, button);
2712 else if (IS_MCDUFFIN(element))
2714 if (!laser.fuse_off)
2716 DrawLaser(0, DL_LASER_DISABLED);
2723 element = get_rotated_element(element, BUTTON_ROTATION(button));
2724 laser.start_angle = get_element_angle(element);
2728 Feld[x][y] = element;
2735 if (!laser.fuse_off)
2738 else if (element == EL_FUSE_ON && laser.fuse_off)
2740 if (x != laser.fuse_x || y != laser.fuse_y)
2743 laser.fuse_off = FALSE;
2744 laser.fuse_x = laser.fuse_y = -1;
2746 DrawGraphic_MM(x, y, IMG_MM_FUSE_ACTIVE);
2749 else if (element == EL_FUSE_ON && !laser.fuse_off && new_button)
2751 laser.fuse_off = TRUE;
2754 laser.overloaded = FALSE;
2756 DrawLaser(0, DL_LASER_DISABLED);
2757 DrawGraphic_MM(x, y, IMG_MM_FUSE);
2759 else if (element == EL_LIGHTBALL)
2763 DrawLaser(0, DL_LASER_ENABLED);
2766 click_delay_value = (new_button ? CLICK_DELAY_FIRST : CLICK_DELAY);
2770 void RotateMirror(int x, int y, int button)
2772 static int hold_x = -1, hold_y = -1;
2774 if (button == MB_RELEASED)
2776 /* release eventually hold auto-rotating mirror */
2783 if (IS_MIRROR(Feld[x][y]) ||
2784 IS_POLAR_CROSS(Feld[x][y]) ||
2785 IS_POLAR(Feld[x][y]) ||
2786 IS_BEAMER(Feld[x][y]) ||
2787 IS_DF_MIRROR(Feld[x][y]) ||
2788 IS_GRID_STEEL_AUTO(Feld[x][y]) ||
2789 IS_GRID_WOOD_AUTO(Feld[x][y]))
2791 Feld[x][y] = get_rotated_element(Feld[x][y], BUTTON_ROTATION(button));
2793 else if (IS_DF_MIRROR_AUTO(Feld[x][y]))
2795 if (button == MB_LEFTBUTTON)
2797 /* left mouse button only for manual adjustment, no auto-rotating;
2798 freeze mirror for until mouse button released */
2802 else if (button == MB_RIGHTBUTTON && (hold_x != x || hold_y != y))
2804 Feld[x][y] = get_rotated_element(Feld[x][y], ROTATE_RIGHT);
2808 if (IS_GRID_STEEL_AUTO(Feld[x][y]) || IS_GRID_WOOD_AUTO(Feld[x][y]))
2810 int edge = Hit[x][y];
2816 DrawLaser(edge - 1, DL_LASER_DISABLED);
2820 else if (ObjHit(x, y, HIT_POS_CENTER))
2822 int edge = Hit[x][y];
2826 Error(ERR_WARN, "RotateMirror: inconsistent field Hit[][]!\n");
2830 DrawLaser(edge - 1, DL_LASER_DISABLED);
2837 if (ObjHit(x, y, HIT_POS_EDGE | HIT_POS_BETWEEN))
2842 if ((IS_BEAMER(Feld[x][y]) ||
2843 IS_POLAR(Feld[x][y]) ||
2844 IS_POLAR_CROSS(Feld[x][y])) && x == ELX && y == ELY)
2848 if (IS_BEAMER(Feld[x][y]))
2851 printf("TEST (%d, %d) [%d] [%d]\n",
2853 laser.beamer_edge, laser.beamer[1].num);
2863 DrawLaser(0, DL_LASER_ENABLED);
2867 void AutoRotateMirrors()
2869 static unsigned int rotate_delay = 0;
2872 if (!FrameReached(&rotate_delay, AUTO_ROTATE_DELAY))
2875 for (x = 0; x < lev_fieldx; x++)
2877 for (y = 0; y < lev_fieldy; y++)
2879 int element = Feld[x][y];
2881 /* do not rotate objects hit by the laser after the game was solved */
2882 if (game_mm.level_solved && Hit[x][y])
2885 if (IS_DF_MIRROR_AUTO(element) ||
2886 IS_GRID_WOOD_AUTO(element) ||
2887 IS_GRID_STEEL_AUTO(element) ||
2888 element == EL_REFRACTOR)
2889 RotateMirror(x, y, MB_RIGHTBUTTON);
2894 boolean ObjHit(int obx, int oby, int bits)
2901 if (bits & HIT_POS_CENTER)
2903 if (CheckLaserPixel(SX + obx + 15,
2908 if (bits & HIT_POS_EDGE)
2910 for (i = 0; i < 4; i++)
2911 if (CheckLaserPixel(SX + obx + 31 * (i % 2),
2912 SY + oby + 31 * (i / 2)))
2916 if (bits & HIT_POS_BETWEEN)
2918 for (i = 0; i < 4; i++)
2919 if (CheckLaserPixel(SX + 4 + obx + 22 * (i % 2),
2920 SY + 4 + oby + 22 * (i / 2)))
2927 void DeletePacMan(int px, int py)
2933 if (game_mm.num_pacman <= 1)
2935 game_mm.num_pacman = 0;
2939 for (i = 0; i < game_mm.num_pacman; i++)
2940 if (game_mm.pacman[i].x == px && game_mm.pacman[i].y == py)
2943 game_mm.num_pacman--;
2945 for (j = i; j < game_mm.num_pacman; j++)
2947 game_mm.pacman[j].x = game_mm.pacman[j + 1].x;
2948 game_mm.pacman[j].y = game_mm.pacman[j + 1].y;
2949 game_mm.pacman[j].dir = game_mm.pacman[j + 1].dir;
2953 void ColorCycling(void)
2955 static int CC, Cc = 0;
2957 static int color, old = 0xF00, new = 0x010, mult = 1;
2958 static unsigned short red, green, blue;
2960 if (color_status == STATIC_COLORS)
2965 if (CC < Cc || CC > Cc + 2)
2969 color = old + new * mult;
2975 if (ABS(mult) == 16)
2985 red = 0x0e00 * ((color & 0xF00) >> 8);
2986 green = 0x0e00 * ((color & 0x0F0) >> 4);
2987 blue = 0x0e00 * ((color & 0x00F));
2988 SetRGB(pen_magicolor[0], red, green, blue);
2990 red = 0x1111 * ((color & 0xF00) >> 8);
2991 green = 0x1111 * ((color & 0x0F0) >> 4);
2992 blue = 0x1111 * ((color & 0x00F));
2993 SetRGB(pen_magicolor[1], red, green, blue);
2997 static void GameActions_MM_Ext(struct MouseActionInfo action, boolean warp_mode)
2999 static unsigned int pacman_delay = 0;
3000 static unsigned int energy_delay = 0;
3001 static unsigned int overload_delay = 0;
3007 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3010 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3012 element = Feld[x][y];
3014 if (!IS_MOVING(x, y) && CAN_MOVE(element))
3015 StartMoving_MM(x, y);
3016 else if (IS_MOVING(x, y))
3017 ContinueMoving_MM(x, y);
3018 else if (IS_EXPLODING(element))
3019 Explode_MM(x, y, Frame[x][y], EX_NORMAL);
3020 else if (element == EL_EXIT_OPENING)
3022 else if (element == EL_GRAY_BALL_OPENING)
3023 OpenSurpriseBall(x, y);
3024 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_ICE)
3026 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_AMOEBA)
3030 AutoRotateMirrors();
3033 /* !!! CHANGE THIS: REDRAW ONLY WHEN NEEDED !!! */
3035 /* redraw after Explode_MM() ... */
3037 DrawLaser(0, DL_LASER_ENABLED);
3038 laser.redraw = FALSE;
3043 if (game_mm.num_pacman && FrameReached(&pacman_delay, PACMAN_MOVE_DELAY))
3047 if (laser.num_damages > MAX_LASER_LEN && !laser.fuse_off)
3049 DrawLaser(0, DL_LASER_DISABLED);
3054 if (FrameReached(&energy_delay, ENERGY_DELAY))
3056 game_mm.energy_left--;
3057 if (game_mm.energy_left >= 0)
3060 BlitBitmap(pix[PIX_DOOR], drawto,
3061 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
3062 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
3063 DX_ENERGY, DY_ENERGY);
3065 redraw_mask |= REDRAW_DOOR_1;
3067 else if (setup.time_limit)
3071 for (i = 15; i >= 0; i--)
3074 SetRGB(pen_ray, 0x0000, 0x0000, i * color_scale);
3076 pen_ray = GetPixelFromRGB(window,
3077 native_mm_level.laser_red * 0x11 * i,
3078 native_mm_level.laser_green * 0x11 * i,
3079 native_mm_level.laser_blue * 0x11 * i);
3081 DrawLaser(0, DL_LASER_ENABLED);
3086 StopSound_MM(SND_MM_GAME_HEALTH_CHARGING);
3089 DrawLaser(0, DL_LASER_DISABLED);
3090 game_mm.game_over = TRUE;
3091 game_mm.game_over_cause = GAME_OVER_NO_ENERGY;
3094 if (Request("Out of magic energy ! Play it again ?",
3095 REQ_ASK | REQ_STAY_CLOSED))
3101 game_status = MAINMENU;
3110 element = laser.dest_element;
3113 if (element != Feld[ELX][ELY])
3115 printf("element == %d, Feld[ELX][ELY] == %d\n",
3116 element, Feld[ELX][ELY]);
3120 if (!laser.overloaded && laser.overload_value == 0 &&
3121 element != EL_BOMB &&
3122 element != EL_MINE &&
3123 element != EL_BALL_GRAY &&
3124 element != EL_BLOCK_STONE &&
3125 element != EL_BLOCK_WOOD &&
3126 element != EL_FUSE_ON &&
3127 element != EL_FUEL_FULL &&
3128 !IS_WALL_ICE(element) &&
3129 !IS_WALL_AMOEBA(element))
3132 if (((laser.overloaded && laser.overload_value < MAX_LASER_OVERLOAD) ||
3133 (!laser.overloaded && laser.overload_value > 0)) &&
3134 FrameReached(&overload_delay, HEALTH_DELAY(laser.overloaded)))
3136 if (laser.overloaded)
3137 laser.overload_value++;
3139 laser.overload_value--;
3141 if (game_mm.cheat_no_overload)
3143 laser.overloaded = FALSE;
3144 laser.overload_value = 0;
3147 game_mm.laser_overload_value = laser.overload_value;
3149 if (laser.overload_value < MAX_LASER_OVERLOAD - 8)
3151 int color_up = 0xFF * laser.overload_value / MAX_LASER_OVERLOAD;
3152 int color_down = 0xFF - color_up;
3155 SetRGB(pen_ray, (laser.overload_value / 6) * color_scale, 0x0000,
3156 (15 - (laser.overload_value / 6)) * color_scale);
3159 GetPixelFromRGB(window,
3160 (native_mm_level.laser_red ? 0xFF : color_up),
3161 (native_mm_level.laser_green ? color_down : 0x00),
3162 (native_mm_level.laser_blue ? color_down : 0x00));
3164 DrawLaser(0, DL_LASER_ENABLED);
3170 if (!laser.overloaded)
3171 StopSound_MM(SND_MM_GAME_HEALTH_CHARGING);
3172 else if (setup.sound_loops)
3173 PlaySoundLoop_MM(SND_MM_GAME_HEALTH_CHARGING);
3175 PlaySound_MM(SND_MM_GAME_HEALTH_CHARGING);
3177 if (laser.overloaded)
3180 BlitBitmap(pix[PIX_DOOR], drawto,
3181 DOOR_GFX_PAGEX4 + XX_OVERLOAD,
3182 DOOR_GFX_PAGEY1 + YY_OVERLOAD + OVERLOAD_YSIZE
3183 - laser.overload_value,
3184 OVERLOAD_XSIZE, laser.overload_value,
3185 DX_OVERLOAD, DY_OVERLOAD + OVERLOAD_YSIZE
3186 - laser.overload_value);
3188 redraw_mask |= REDRAW_DOOR_1;
3193 BlitBitmap(pix[PIX_DOOR], drawto,
3194 DOOR_GFX_PAGEX5 + XX_OVERLOAD, DOOR_GFX_PAGEY1 + YY_OVERLOAD,
3195 OVERLOAD_XSIZE, OVERLOAD_YSIZE - laser.overload_value,
3196 DX_OVERLOAD, DY_OVERLOAD);
3198 redraw_mask |= REDRAW_DOOR_1;
3201 if (laser.overload_value == MAX_LASER_OVERLOAD)
3205 for (i = 15; i >= 0; i--)
3208 SetRGB(pen_ray, i * color_scale, 0x0000, 0x0000);
3211 pen_ray = GetPixelFromRGB(window, 0x11 * i, 0x00, 0x00);
3213 DrawLaser(0, DL_LASER_ENABLED);
3218 DrawLaser(0, DL_LASER_DISABLED);
3220 game_mm.game_over = TRUE;
3221 game_mm.game_over_cause = GAME_OVER_OVERLOADED;
3224 if (Request("Magic spell hit Mc Duffin ! Play it again ?",
3225 REQ_ASK | REQ_STAY_CLOSED))
3231 game_status = MAINMENU;
3245 if (element == EL_BOMB && CT > 75)
3247 if (game_mm.cheat_no_explosion)
3251 laser.num_damages--;
3252 DrawLaser(0, DL_LASER_DISABLED);
3253 laser.num_edges = 0;
3258 laser.dest_element = EL_EXPLODING_OPAQUE;
3262 laser.num_damages--;
3263 DrawLaser(0, DL_LASER_DISABLED);
3265 laser.num_edges = 0;
3266 Bang_MM(laser.start_edge.x, laser.start_edge.y);
3268 if (Request("Bomb killed Mc Duffin ! Play it again ?",
3269 REQ_ASK | REQ_STAY_CLOSED))
3275 game_status = MAINMENU;
3283 if (element == EL_FUSE_ON && CT > 25)
3285 laser.fuse_off = TRUE;
3289 DrawLaser(0, DL_LASER_DISABLED);
3290 DrawGraphic_MM(ELX, ELY, IMG_MM_FUSE);
3293 if (element == EL_BALL_GRAY && CT > 75)
3295 static int new_elements[] =
3298 EL_MIRROR_FIXED_START,
3300 EL_POLAR_CROSS_START,
3306 int num_new_elements = sizeof(new_elements) / sizeof(int);
3307 int new_element = new_elements[RND(num_new_elements)];
3309 Store[ELX][ELY] = new_element + RND(get_num_elements(new_element));
3310 Feld[ELX][ELY] = EL_GRAY_BALL_OPENING;
3312 /* !!! CHECK AGAIN: Laser on Polarizer !!! */
3323 element = EL_MIRROR_START + RND(16);
3329 element = (rnd == 0 ? EL_KETTLE : rnd == 1 ? EL_BOMB : EL_PRISM);
3336 element = (rnd == 0 ? EL_FUSE_ON :
3337 rnd >= 1 && rnd <= 4 ? EL_PACMAN_RIGHT + rnd - 1 :
3338 rnd >= 5 && rnd <= 20 ? EL_POLAR_START + rnd - 5 :
3339 rnd >= 21 && rnd <= 24 ? EL_POLAR_CROSS_START + rnd - 21 :
3340 EL_MIRROR_FIXED_START + rnd - 25);
3345 graphic = el2gfx(element);
3347 for (i = 0; i < 50; i++)
3353 BlitBitmap(pix[PIX_BACK], drawto,
3354 SX + (graphic % GFX_PER_LINE) * TILEX + x,
3355 SY + (graphic / GFX_PER_LINE) * TILEY + y, 6, 6,
3356 SX + ELX * TILEX + x,
3357 SY + ELY * TILEY + y);
3359 MarkTileDirty(ELX, ELY);
3362 DrawLaser(0, DL_LASER_ENABLED);
3367 Feld[ELX][ELY] = element;
3368 DrawField_MM(ELX, ELY);
3371 printf("NEW ELEMENT: (%d, %d)\n", ELX, ELY);
3374 /* above stuff: GRAY BALL -> PRISM !!! */
3376 LX = ELX * TILEX + 14;
3377 LY = ELY * TILEY + 14;
3378 if (laser.current_angle == (laser.current_angle >> 1) << 1)
3385 laser.num_edges -= 2;
3386 laser.num_damages--;
3390 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
3391 if (laser.damage[i].is_mirror)
3395 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3397 DrawLaser(0, DL_LASER_DISABLED);
3399 DrawLaser(0, DL_LASER_DISABLED);
3405 printf("TEST ELEMENT: %d\n", Feld[0][0]);
3412 if (IS_WALL_ICE(element) && CT > 50)
3414 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_SHRINKING);
3417 Feld[ELX][ELY] = Feld[ELX][ELY] - EL_WALL_ICE + EL_WALL_CHANGING;
3418 Store[ELX][ELY] = EL_WALL_ICE;
3419 Store2[ELX][ELY] = laser.wall_mask;
3421 laser.dest_element = Feld[ELX][ELY];
3426 for (i = 0; i < 5; i++)
3432 Feld[ELX][ELY] &= (laser.wall_mask ^ 0xFF);
3436 DrawWallsAnimation_MM(ELX, ELY, Feld[ELX][ELY], phase, laser.wall_mask);
3441 if (Feld[ELX][ELY] == EL_WALL_ICE)
3442 Feld[ELX][ELY] = EL_EMPTY;
3446 LX = laser.edge[laser.num_edges].x - (SX + 2);
3447 LY = laser.edge[laser.num_edges].y - (SY + 2);
3450 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
3451 if (laser.damage[i].is_mirror)
3455 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3457 DrawLaser(0, DL_LASER_DISABLED);
3464 if (IS_WALL_AMOEBA(element) && CT > 60)
3466 int k1, k2, k3, dx, dy, de, dm;
3467 int element2 = Feld[ELX][ELY];
3469 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element))
3472 for (i = laser.num_damages - 1; i >= 0; i--)
3473 if (laser.damage[i].is_mirror)
3476 r = laser.num_edges;
3477 d = laser.num_damages;
3484 DrawLaser(laser.damage[k1].edge - 1, DL_LASER_DISABLED);
3487 DrawLaser(0, DL_LASER_ENABLED);
3490 x = laser.damage[k1].x;
3491 y = laser.damage[k1].y;
3496 for (i = 0; i < 4; i++)
3498 if (laser.wall_mask & (1 << i))
3500 if (CheckLaserPixel(SX + ELX * TILEX + 14 + (i % 2) * 2,
3501 SY + ELY * TILEY + 31 * (i / 2)))
3504 if (CheckLaserPixel(SX + ELX * TILEX + 31 * (i % 2),
3505 SY + ELY * TILEY + 14 + (i / 2) * 2))
3512 for (i = 0; i < 4; i++)
3514 if (laser.wall_mask & (1 << i))
3516 if (CheckLaserPixel(SX + ELX * TILEX + 31 * (i % 2),
3517 SY + ELY * TILEY + 31 * (i / 2)))
3524 if (laser.num_beamers > 0 ||
3525 k1 < 1 || k2 < 4 || k3 < 4 ||
3526 CheckLaserPixel(SX + ELX * TILEX + 14,
3527 SY + ELY * TILEY + 14))
3529 laser.num_edges = r;
3530 laser.num_damages = d;
3532 DrawLaser(0, DL_LASER_DISABLED);
3535 Feld[ELX][ELY] = element | laser.wall_mask;
3539 de = Feld[ELX][ELY];
3540 dm = laser.wall_mask;
3544 int x = ELX, y = ELY;
3545 int wall_mask = laser.wall_mask;
3548 DrawLaser(0, DL_LASER_ENABLED);
3550 PlayLevelSound_MM(dx, dy, element, MM_ACTION_GROWING);
3552 Feld[x][y] = Feld[x][y] - EL_WALL_AMOEBA + EL_WALL_CHANGING;
3553 Store[x][y] = EL_WALL_AMOEBA;
3554 Store2[x][y] = wall_mask;
3560 DrawWallsAnimation_MM(dx, dy, de, 4, dm);
3562 DrawLaser(0, DL_LASER_ENABLED);
3564 PlayLevelSound_MM(dx, dy, element, MM_ACTION_GROWING);
3566 for (i = 4; i >= 0; i--)
3568 DrawWallsAnimation_MM(dx, dy, de, i, dm);
3574 DrawLaser(0, DL_LASER_ENABLED);
3579 if ((element == EL_BLOCK_WOOD || element == EL_BLOCK_STONE) &&
3580 laser.stops_inside_element && CT > 75)
3585 if (ABS(XS) > ABS(YS))
3592 for (i = 0; i < 4; i++)
3599 x = ELX + Step[k * 4].x;
3600 y = ELY + Step[k * 4].y;
3602 if (!IN_LEV_FIELD(x, y) || Feld[x][y] != EL_EMPTY)
3605 if (ObjHit(x, y, HIT_POS_CENTER | HIT_POS_EDGE | HIT_POS_BETWEEN))
3613 laser.overloaded = (element == EL_BLOCK_STONE);
3618 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_PUSHING);
3621 Feld[x][y] = element;
3623 DrawGraphic_MM(ELX, ELY, IMG_EMPTY);
3626 if (element == EL_BLOCK_STONE && Box[ELX][ELY])
3628 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
3629 DrawLaser(laser.num_edges - 1, DL_LASER_ENABLED);
3637 if (element == EL_FUEL_FULL && CT > 10)
3639 for (i = game_mm.energy_left; i <= MAX_LASER_ENERGY; i+=2)
3642 BlitBitmap(pix[PIX_DOOR], drawto,
3643 DOOR_GFX_PAGEX4 + XX_ENERGY,
3644 DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
3645 ENERGY_XSIZE, i, DX_ENERGY,
3646 DY_ENERGY + ENERGY_YSIZE - i);
3649 redraw_mask |= REDRAW_DOOR_1;
3655 game_mm.energy_left = MAX_LASER_ENERGY;
3656 Feld[ELX][ELY] = EL_FUEL_EMPTY;
3657 DrawField_MM(ELX, ELY);
3659 DrawLaser(0, DL_LASER_ENABLED);
3667 void GameActions_MM(struct MouseActionInfo action, boolean warp_mode)
3669 ClickElement(action.lx, action.ly, action.button);
3671 GameActions_MM_Ext(action, warp_mode);
3677 int mx, my, ox, oy, nx, ny;
3681 if (++p >= game_mm.num_pacman)
3684 game_mm.pacman[p].dir--;
3686 for (l = 1; l < 5; l++)
3688 game_mm.pacman[p].dir++;
3690 if (game_mm.pacman[p].dir > 4)
3691 game_mm.pacman[p].dir = 1;
3693 if (game_mm.pacman[p].dir % 2)
3696 my = game_mm.pacman[p].dir - 2;
3701 mx = 3 - game_mm.pacman[p].dir;
3704 ox = game_mm.pacman[p].x;
3705 oy = game_mm.pacman[p].y;
3708 element = Feld[nx][ny];
3710 if (nx < 0 || nx > 15 || ny < 0 || ny > 11)
3713 if (!IS_EATABLE4PACMAN(element))
3716 if (ObjHit(nx, ny, HIT_POS_CENTER))
3719 Feld[ox][oy] = EL_EMPTY;
3721 EL_PACMAN_RIGHT - 1 +
3722 (game_mm.pacman[p].dir - 1 +
3723 (game_mm.pacman[p].dir % 2) * 2);
3725 game_mm.pacman[p].x = nx;
3726 game_mm.pacman[p].y = ny;
3728 DrawGraphic_MM(ox, oy, IMG_EMPTY);
3730 if (element != EL_EMPTY)
3732 int graphic = el2gfx(Feld[nx][ny]);
3737 getGraphicSource(graphic, 0, &bitmap, &src_x, &src_y);
3740 ox = SX + ox * TILEX;
3741 oy = SY + oy * TILEY;
3743 for (i = 1; i < 33; i += 2)
3744 BlitBitmap(bitmap, window,
3745 src_x, src_y, TILEX, TILEY,
3746 ox + i * mx, oy + i * my);
3747 Ct = Ct + FrameCounter - CT;
3750 DrawField_MM(nx, ny);
3753 if (!laser.fuse_off)
3755 DrawLaser(0, DL_LASER_ENABLED);
3757 if (ObjHit(nx, ny, HIT_POS_BETWEEN))
3759 AddDamagedField(nx, ny);
3761 laser.damage[laser.num_damages - 1].edge = 0;
3765 if (element == EL_BOMB)
3766 DeletePacMan(nx, ny);
3768 if (IS_WALL_AMOEBA(element) &&
3769 (LX + 2 * XS) / TILEX == nx &&
3770 (LY + 2 * YS) / TILEY == ny)
3783 boolean raise_level = FALSE;
3786 if (local_player->MovPos)
3789 local_player->LevelSolved = FALSE;
3792 if (game_mm.energy_left)
3794 if (setup.sound_loops)
3795 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3796 SND_CTRL_PLAY_LOOP);
3798 while (game_mm.energy_left > 0)
3800 if (!setup.sound_loops)
3801 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3804 if (game_mm.energy_left > 0 && !(game_mm.energy_left % 10))
3805 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3810 game_mm.energy_left--;
3811 if (game_mm.energy_left >= 0)
3814 BlitBitmap(pix[PIX_DOOR], drawto,
3815 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
3816 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
3817 DX_ENERGY, DY_ENERGY);
3819 redraw_mask |= REDRAW_DOOR_1;
3826 if (setup.sound_loops)
3827 StopSound(SND_SIRR);
3829 else if (native_mm_level.time == 0) /* level without time limit */
3831 if (setup.sound_loops)
3832 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3833 SND_CTRL_PLAY_LOOP);
3835 while (TimePlayed < 999)
3837 if (!setup.sound_loops)
3838 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3839 if (TimePlayed < 999 && !(TimePlayed % 10))
3840 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3841 if (TimePlayed < 900 && !(TimePlayed % 10))
3847 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
3854 if (setup.sound_loops)
3855 StopSound(SND_SIRR);
3862 CloseDoor(DOOR_CLOSE_1);
3864 Request("Level solved !", REQ_CONFIRM);
3866 if (level_nr == leveldir_current->handicap_level)
3868 leveldir_current->handicap_level++;
3869 SaveLevelSetup_SeriesInfo();
3872 if (level_editor_test_game)
3873 game_mm.score = -1; /* no highscore when playing from editor */
3874 else if (level_nr < leveldir_current->last_level)
3875 raise_level = TRUE; /* advance to next level */
3877 if ((hi_pos = NewHiScore_MM()) >= 0)
3879 game_status = HALLOFFAME;
3881 // DrawHallOfFame(hi_pos);
3888 game_status = MAINMENU;
3904 // LoadScore(level_nr);
3906 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
3907 game_mm.score < highscore[MAX_SCORE_ENTRIES - 1].Score)
3910 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3912 if (game_mm.score > highscore[k].Score)
3914 /* player has made it to the hall of fame */
3916 if (k < MAX_SCORE_ENTRIES - 1)
3918 int m = MAX_SCORE_ENTRIES - 1;
3921 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3922 if (!strcmp(setup.player_name, highscore[l].Name))
3924 if (m == k) /* player's new highscore overwrites his old one */
3928 for (l = m; l>k; l--)
3930 strcpy(highscore[l].Name, highscore[l - 1].Name);
3931 highscore[l].Score = highscore[l - 1].Score;
3938 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3939 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3940 highscore[k].Score = game_mm.score;
3947 else if (!strncmp(setup.player_name, highscore[k].Name,
3948 MAX_PLAYER_NAME_LEN))
3949 break; /* player already there with a higher score */
3954 // if (position >= 0)
3955 // SaveScore(level_nr);
3960 static void InitMovingField_MM(int x, int y, int direction)
3962 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3963 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3965 MovDir[x][y] = direction;
3966 MovDir[newx][newy] = direction;
3968 if (Feld[newx][newy] == EL_EMPTY)
3969 Feld[newx][newy] = EL_BLOCKED;
3972 static void Moving2Blocked_MM(int x, int y, int *goes_to_x, int *goes_to_y)
3974 int direction = MovDir[x][y];
3975 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3976 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3982 static void Blocked2Moving_MM(int x, int y,
3983 int *comes_from_x, int *comes_from_y)
3985 int oldx = x, oldy = y;
3986 int direction = MovDir[x][y];
3988 if (direction == MV_LEFT)
3990 else if (direction == MV_RIGHT)
3992 else if (direction == MV_UP)
3994 else if (direction == MV_DOWN)
3997 *comes_from_x = oldx;
3998 *comes_from_y = oldy;
4001 static int MovingOrBlocked2Element_MM(int x, int y)
4003 int element = Feld[x][y];
4005 if (element == EL_BLOCKED)
4009 Blocked2Moving_MM(x, y, &oldx, &oldy);
4011 return Feld[oldx][oldy];
4018 static void RemoveField(int x, int y)
4020 Feld[x][y] = EL_EMPTY;
4027 static void RemoveMovingField_MM(int x, int y)
4029 int oldx = x, oldy = y, newx = x, newy = y;
4031 if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
4034 if (IS_MOVING(x, y))
4036 Moving2Blocked_MM(x, y, &newx, &newy);
4037 if (Feld[newx][newy] != EL_BLOCKED)
4040 else if (Feld[x][y] == EL_BLOCKED)
4042 Blocked2Moving_MM(x, y, &oldx, &oldy);
4043 if (!IS_MOVING(oldx, oldy))
4047 Feld[oldx][oldy] = EL_EMPTY;
4048 Feld[newx][newy] = EL_EMPTY;
4049 MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
4050 MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
4052 DrawLevelField_MM(oldx, oldy);
4053 DrawLevelField_MM(newx, newy);
4056 void PlaySoundLevel(int x, int y, int sound_nr)
4058 int sx = SCREENX(x), sy = SCREENY(y);
4060 int silence_distance = 8;
4062 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
4063 (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
4066 if (!IN_LEV_FIELD(x, y) ||
4067 sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
4068 sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
4071 volume = SOUND_MAX_VOLUME;
4074 stereo = (sx - SCR_FIELDX/2) * 12;
4076 stereo = SOUND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
4077 if (stereo > SOUND_MAX_RIGHT)
4078 stereo = SOUND_MAX_RIGHT;
4079 if (stereo < SOUND_MAX_LEFT)
4080 stereo = SOUND_MAX_LEFT;
4083 if (!IN_SCR_FIELD(sx, sy))
4085 int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
4086 int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
4088 volume -= volume * (dx > dy ? dx : dy) / silence_distance;
4091 PlaySoundExt(sound_nr, volume, stereo, SND_CTRL_PLAY_SOUND);
4094 static void RaiseScore_MM(int value)
4096 game_mm.score += value;
4099 DrawText(DX_SCORE, DY_SCORE, int2str(game_mm.score, 4),
4104 void RaiseScoreElement_MM(int element)
4109 RaiseScore_MM(native_mm_level.score[SC_PACMAN]);
4113 RaiseScore_MM(native_mm_level.score[SC_KEY]);