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 /* forward declaration for internal use */
82 static int MovingOrBlocked2Element_MM(int, int);
83 static void Bang_MM(int, int);
84 static void RaiseScore_MM(int);
85 static void RemoveMovingField_MM(int, int);
86 static void InitMovingField_MM(int, int, int);
87 static void ContinueMoving_MM(int, int);
88 static void Moving2Blocked_MM(int, int, int *, int *);
92 // =============================================================================
93 // TMP_dump_masks_MM (code)
94 // =============================================================================
96 void TMP_dump_masks_MM()
100 printf("static const char mm_masks[10][16][16] =\n");
103 for (i = EL_MM_MASK_MCDUFFIN_RIGHT; i <= EL_MM_MASK_CIRCLE; i++)
105 int graphic_mask = el2gfx(i);
110 getGraphicSource(graphic_mask, 0, &bitmap, &src_x, &src_y);
114 for (y = 0; y < TILEY; y += 2)
118 for (x = 0; x < TILEX; x += 2)
120 Pixel pixel = GetPixel(bitmap, src_x + x, src_y + y);
122 printf("%c", (pixel ? 'X' : ' '));
135 // =============================================================================
136 // TMP_dump_masks_MM (result)
137 // =============================================================================
139 static const char mm_masks[10][16][16 + 1] =
323 static int get_element_angle(int element)
325 int element_phase = get_element_phase(element);
327 if (IS_MIRROR_FIXED(element) ||
328 IS_MCDUFFIN(element) ||
330 IS_RECEIVER(element))
331 return 4 * element_phase;
333 return element_phase;
336 static int get_opposite_angle(int angle)
338 int opposite_angle = angle + ANG_RAY_180;
340 /* make sure "opposite_angle" is in valid interval [0, 15] */
341 return (opposite_angle + 16) % 16;
344 static int get_mirrored_angle(int laser_angle, int mirror_angle)
346 int reflected_angle = 16 - laser_angle + mirror_angle;
348 /* make sure "reflected_angle" is in valid interval [0, 15] */
349 return (reflected_angle + 16) % 16;
352 static void InitMovDir_MM(int x, int y)
354 int element = Feld[x][y];
355 static int direction[3][4] =
357 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
358 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
359 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
364 case EL_PACMAN_RIGHT:
368 Feld[x][y] = EL_PACMAN;
369 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
377 static void InitField(int x, int y, boolean init_game)
379 int element = Feld[x][y];
384 Feld[x][y] = EL_EMPTY;
389 if (native_mm_level.auto_count_kettles)
390 game_mm.kettles_still_needed++;
393 case EL_LIGHTBULB_OFF:
394 game_mm.lights_still_needed++;
398 if (IS_MIRROR(element) ||
399 IS_BEAMER_OLD(element) ||
400 IS_BEAMER(element) ||
402 IS_POLAR_CROSS(element) ||
403 IS_DF_MIRROR(element) ||
404 IS_DF_MIRROR_AUTO(element) ||
405 IS_GRID_STEEL_AUTO(element) ||
406 IS_GRID_WOOD_AUTO(element) ||
407 IS_FIBRE_OPTIC(element))
409 if (IS_BEAMER_OLD(element))
411 Feld[x][y] = EL_BEAMER_BLUE_START + (element - EL_BEAMER_START);
412 element = Feld[x][y];
415 if (!IS_FIBRE_OPTIC(element))
417 static int steps_grid_auto = 0;
419 if (game_mm.num_cycle == 0) /* initialize cycle steps for grids */
420 steps_grid_auto = RND(16) * (RND(2) ? -1 : +1);
422 if (IS_GRID_STEEL_AUTO(element) ||
423 IS_GRID_WOOD_AUTO(element))
424 game_mm.cycle[game_mm.num_cycle].steps = steps_grid_auto;
426 game_mm.cycle[game_mm.num_cycle].steps = RND(16) * (RND(2) ? -1 : +1);
428 game_mm.cycle[game_mm.num_cycle].x = x;
429 game_mm.cycle[game_mm.num_cycle].y = y;
433 if (IS_BEAMER(element) || IS_FIBRE_OPTIC(element))
435 int beamer_nr = BEAMER_NR(element);
436 int nr = laser.beamer[beamer_nr][0].num;
438 laser.beamer[beamer_nr][nr].x = x;
439 laser.beamer[beamer_nr][nr].y = y;
440 laser.beamer[beamer_nr][nr].num = 1;
443 else if (IS_PACMAN(element))
447 else if (IS_MCDUFFIN(element) || IS_LASER(element))
449 laser.start_edge.x = x;
450 laser.start_edge.y = y;
451 laser.start_angle = get_element_angle(element);
458 static void InitCycleElements_RotateSingleStep()
462 if (game_mm.num_cycle == 0) /* no elements to cycle */
465 for (i = 0; i < game_mm.num_cycle; i++)
467 int x = game_mm.cycle[i].x;
468 int y = game_mm.cycle[i].y;
469 int step = SIGN(game_mm.cycle[i].steps);
470 int last_element = Feld[x][y];
471 int next_element = get_rotated_element(last_element, step);
473 if (!game_mm.cycle[i].steps)
476 Feld[x][y] = next_element;
479 game_mm.cycle[i].steps -= step;
483 static void InitLaser()
485 int start_element = Feld[laser.start_edge.x][laser.start_edge.y];
486 int step = (IS_LASER(start_element) ? 4 : 0);
488 LX = laser.start_edge.x * TILEX;
489 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
492 LX += (laser.start_angle == ANG_RAY_RIGHT ? 28 + step : 0 - step);
494 LY = laser.start_edge.y * TILEY;
495 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
496 LY += (laser.start_angle == ANG_RAY_DOWN ? 28 + step : 0 - step);
500 XS = 2 * Step[laser.start_angle].x;
501 YS = 2 * Step[laser.start_angle].y;
503 laser.current_angle = laser.start_angle;
505 laser.num_damages = 0;
507 laser.num_beamers = 0;
508 laser.beamer_edge[0] = 0;
510 laser.dest_element = EL_EMPTY;
513 AddLaserEdge(LX, LY); /* set laser starting edge */
515 pen_ray = GetPixelFromRGB(window,
516 native_mm_level.laser_red * 0xFF,
517 native_mm_level.laser_green * 0xFF,
518 native_mm_level.laser_blue * 0xFF);
521 void InitGameEngine_MM()
529 /* set global game control values */
530 game_mm.num_cycle = 0;
531 game_mm.num_pacman = 0;
534 game_mm.energy_left = 0; // later set to "native_mm_level.time"
535 game_mm.kettles_still_needed =
536 (native_mm_level.auto_count_kettles ? 0 : native_mm_level.kettles_needed);
537 game_mm.lights_still_needed = 0;
538 game_mm.num_keys = 0;
540 game_mm.level_solved = FALSE;
541 game_mm.game_over = FALSE;
542 game_mm.game_over_cause = 0;
544 game_mm.laser_overload_value = 0;
546 /* set global laser control values (must be set before "InitLaser()") */
547 laser.start_edge.x = 0;
548 laser.start_edge.y = 0;
549 laser.start_angle = 0;
551 for (i = 0; i < MAX_NUM_BEAMERS; i++)
552 laser.beamer[i][0].num = laser.beamer[i][1].num = 0;
554 laser.overloaded = FALSE;
555 laser.overload_value = 0;
556 laser.fuse_off = FALSE;
557 laser.fuse_x = laser.fuse_y = -1;
559 laser.dest_element = EL_EMPTY;
564 for (x = 0; x < lev_fieldx; x++)
566 for (y = 0; y < lev_fieldy; y++)
568 Feld[x][y] = Ur[x][y];
569 Hit[x][y] = Box[x][y] = 0;
571 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
572 Store[x][y] = Store2[x][y] = 0;
576 InitField(x, y, TRUE);
581 CloseDoor(DOOR_CLOSE_1);
587 void InitGameActions_MM()
589 int num_init_game_frames = INIT_GAME_ACTIONS_DELAY;
590 int cycle_steps_done = 0;
596 /* copy default game door content to main double buffer */
597 BlitBitmap(pix[PIX_DOOR], drawto,
598 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
602 DrawText(DX_LEVEL, DY_LEVEL,
603 int2str(level_nr, 2), FONT_TEXT_2);
604 DrawText(DX_KETTLES, DY_KETTLES,
605 int2str(game_mm.kettles_still_needed, 3), FONT_TEXT_2);
606 DrawText(DX_SCORE, DY_SCORE,
607 int2str(game_mm.score, 4), FONT_TEXT_2);
616 /* copy actual game door content to door double buffer for OpenDoor() */
617 BlitBitmap(drawto, pix[PIX_DB_DOOR],
618 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
622 OpenDoor(DOOR_OPEN_ALL);
625 for (i = 0; i <= num_init_game_frames; i++)
627 if (i == num_init_game_frames)
628 StopSound_MM(SND_MM_GAME_LEVELTIME_CHARGING);
629 else if (setup.sound_loops)
630 PlaySoundLoop_MM(SND_MM_GAME_LEVELTIME_CHARGING);
632 PlaySound_MM(SND_MM_GAME_LEVELTIME_CHARGING);
634 game_mm.energy_left = native_mm_level.time * i / num_init_game_frames;
636 UpdateAndDisplayGameControlValues();
638 while (cycle_steps_done < NUM_INIT_CYCLE_STEPS * i / num_init_game_frames)
640 InitCycleElements_RotateSingleStep();
650 if (setup.quick_doors)
656 if (setup.sound_music && num_bg_loops)
657 PlayMusic(level_nr % num_bg_loops);
663 void AddLaserEdge(int lx, int ly)
665 if (lx < -2 || ly < -2 || lx >= SXSIZE + 2 || ly >= SYSIZE + 2)
667 Error(ERR_WARN, "AddLaserEdge: out of bounds: %d, %d", lx, ly);
672 laser.edge[laser.num_edges].x = SX + 2 + lx;
673 laser.edge[laser.num_edges].y = SY + 2 + ly;
679 void AddDamagedField(int ex, int ey)
681 laser.damage[laser.num_damages].is_mirror = FALSE;
682 laser.damage[laser.num_damages].angle = laser.current_angle;
683 laser.damage[laser.num_damages].edge = laser.num_edges;
684 laser.damage[laser.num_damages].x = ex;
685 laser.damage[laser.num_damages].y = ey;
695 int last_x = laser.edge[laser.num_edges - 1].x - SX - 2;
696 int last_y = laser.edge[laser.num_edges - 1].y - SY - 2;
698 return ((x - last_x) * XS < 0 || (y - last_y) * YS < 0);
704 static int getMaskFromElement(int element)
706 if (IS_GRID(element))
707 return IMG_MM_MASK_GRID_1 + get_element_phase(element);
708 else if (IS_MCDUFFIN(element))
709 return IMG_MM_MASK_MCDUFFIN_RIGHT + get_element_phase(element);
710 else if (IS_RECTANGLE(element) || IS_DF_GRID(element))
711 return IMG_MM_MASK_RECTANGLE;
713 return IMG_MM_MASK_CIRCLE;
721 printf("ScanPixel: start scanning at (%d, %d) [%d, %d] [%d, %d]\n",
722 LX, LY, LX / TILEX, LY / TILEY, LX % TILEX, LY % TILEY);
725 /* follow laser beam until it hits something (at least the screen border) */
726 while (hit_mask == HIT_MASK_NO_HIT)
732 if (SX + LX < REAL_SX || SX + LX >= REAL_SX + FULL_SXSIZE ||
733 SY + LY < REAL_SY || SY + LY >= REAL_SY + FULL_SYSIZE)
735 printf("ScanPixel: touched screen border!\n");
741 for (i = 0; i < 4; i++)
743 int px = LX + (i % 2) * 2;
744 int py = LY + (i / 2) * 2;
747 int lx = (px + TILEX) / TILEX - 1; /* ...+TILEX...-1 to get correct */
748 int ly = (py + TILEY) / TILEY - 1; /* negative values! */
751 if (IN_LEV_FIELD(lx, ly))
753 int element = Feld[lx][ly];
755 if (element == EL_EMPTY || element == EL_EXPLODING_TRANSP)
759 else if (IS_WALL(element) || IS_WALL_CHANGING(element))
761 int pos = dy / MINI_TILEY * 2 + dx / MINI_TILEX;
763 pixel = ((element & (1 << pos)) ? 1 : 0);
767 int pos = getMaskFromElement(element) - IMG_MM_MASK_MCDUFFIN_RIGHT;
769 pixel = (mm_masks[pos][dy / 2][dx / 2] == 'X' ? 1 : 0);
774 pixel = (SX + px < REAL_SX || SX + px >= REAL_SX + FULL_SXSIZE ||
775 SY + py < REAL_SY || SY + py >= REAL_SY + FULL_SYSIZE);
778 if ((Sign[laser.current_angle] & (1 << i)) && pixel)
779 hit_mask |= (1 << i);
782 if (hit_mask == HIT_MASK_NO_HIT)
784 /* hit nothing -- go on with another step */
796 int end = 0, rf = laser.num_edges;
798 /* do not scan laser again after the game was lost for whatever reason */
799 if (game_mm.game_over)
802 laser.overloaded = FALSE;
803 laser.stops_inside_element = FALSE;
805 DrawLaser(0, DL_LASER_ENABLED);
808 printf("Start scanning with LX == %d, LY == %d, XS == %d, YS == %d\n",
816 if (laser.num_edges > MAX_LASER_LEN || laser.num_damages > MAX_LASER_LEN)
819 laser.overloaded = TRUE;
824 hit_mask = ScanPixel();
827 printf("Hit something at LX == %d, LY == %d, XS == %d, YS == %d\n",
831 /* hit something -- check out what it was */
832 ELX = (LX + XS) / TILEX;
833 ELY = (LY + YS) / TILEY;
836 printf("hit_mask (1) == '%x' (%d, %d) (%d, %d)\n",
837 hit_mask, LX, LY, ELX, ELY);
840 if (!IN_LEV_FIELD(ELX, ELY) || !IN_PIX_FIELD(LX, LY))
843 laser.dest_element = element;
848 if (hit_mask == (HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT))
850 /* we have hit the top-right and bottom-left element --
851 choose the bottom-left one */
852 /* !!! THIS CAN BE DONE MORE INTELLIGENTLY, FOR EXAMPLE, IF ONE
853 ELEMENT WAS STEEL AND THE OTHER ONE WAS ICE => ALWAYS CHOOSE
854 THE ICE AND MELT IT AWAY INSTEAD OF OVERLOADING LASER !!! */
855 ELX = (LX - 2) / TILEX;
856 ELY = (LY + 2) / TILEY;
859 if (hit_mask == (HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT))
861 /* we have hit the top-left and bottom-right element --
862 choose the top-left one */
863 /* !!! SEE ABOVE !!! */
864 ELX = (LX - 2) / TILEX;
865 ELY = (LY - 2) / TILEY;
869 printf("hit_mask (2) == '%x' (%d, %d) (%d, %d)\n",
870 hit_mask, LX, LY, ELX, ELY);
873 element = Feld[ELX][ELY];
874 laser.dest_element = element;
877 printf("Hit element %d at (%d, %d) [%d, %d] [%d, %d] [%d]\n",
880 LX % TILEX, LY % TILEY,
885 if (!IN_LEV_FIELD(ELX, ELY))
886 printf("WARNING! (1) %d, %d (%d)\n", ELX, ELY, element);
889 if (element == EL_EMPTY)
891 if (!HitOnlyAnEdge(element, hit_mask))
894 else if (element == EL_FUSE_ON)
896 if (HitPolarizer(element, hit_mask))
899 else if (IS_GRID(element) || IS_DF_GRID(element))
901 if (HitPolarizer(element, hit_mask))
904 else if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD ||
905 element == EL_GATE_STONE || element == EL_GATE_WOOD)
907 if (HitBlock(element, hit_mask))
914 else if (IS_MCDUFFIN(element))
916 if (HitLaserSource(element, hit_mask))
919 else if ((element >= EL_EXIT_CLOSED && element <= EL_EXIT_OPEN) ||
920 IS_RECEIVER(element))
922 if (HitLaserDestination(element, hit_mask))
925 else if (IS_WALL(element))
927 if (IS_WALL_STEEL(element) || IS_DF_WALL_STEEL(element))
929 if (HitReflectingWalls(element, hit_mask))
934 if (HitAbsorbingWalls(element, hit_mask))
940 if (HitElement(element, hit_mask))
945 DrawLaser(rf - 1, DL_LASER_ENABLED);
946 rf = laser.num_edges;
950 if (laser.dest_element != Feld[ELX][ELY])
952 printf("ALARM: laser.dest_element == %d, Feld[ELX][ELY] == %d\n",
953 laser.dest_element, Feld[ELX][ELY]);
957 if (!end && !laser.stops_inside_element && !StepBehind())
960 printf("ScanLaser: Go one step back\n");
966 AddLaserEdge(LX, LY);
970 DrawLaser(rf - 1, DL_LASER_ENABLED);
972 Ct = CT = FrameCounter;
975 if (!IN_LEV_FIELD(ELX, ELY))
976 printf("WARNING! (2) %d, %d\n", ELX, ELY);
980 void DrawLaserExt(int start_edge, int num_edges, int mode)
986 printf("DrawLaserExt: start_edge, num_edges, mode == %d, %d, %d\n",
987 start_edge, num_edges, mode);
992 Error(ERR_WARN, "DrawLaserExt: start_edge < 0");
999 Error(ERR_WARN, "DrawLaserExt: num_edges < 0");
1005 if (mode == DL_LASER_DISABLED)
1007 printf("DrawLaser: Delete laser from edge %d\n", start_edge);
1011 /* now draw the laser to the backbuffer and (if enabled) to the screen */
1012 DrawLines(drawto, &laser.edge[start_edge], num_edges,
1013 (mode == DL_LASER_ENABLED ? pen_ray : pen_bg));
1015 redraw_mask |= REDRAW_FIELD;
1017 if (mode == DL_LASER_ENABLED)
1020 /* after the laser was deleted, the "damaged" graphics must be restored */
1021 if (laser.num_damages)
1023 int damage_start = 0;
1026 /* determine the starting edge, from which graphics need to be restored */
1029 for (i = 0; i < laser.num_damages; i++)
1031 if (laser.damage[i].edge == start_edge + 1)
1040 /* restore graphics from this starting edge to the end of damage list */
1041 for (i = damage_start; i < laser.num_damages; i++)
1043 int lx = laser.damage[i].x;
1044 int ly = laser.damage[i].y;
1045 int element = Feld[lx][ly];
1047 if (Hit[lx][ly] == laser.damage[i].edge)
1048 if (!((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1051 if (Box[lx][ly] == laser.damage[i].edge)
1054 if (IS_DRAWABLE(element))
1055 DrawField_MM(lx, ly);
1058 elx = laser.damage[damage_start].x;
1059 ely = laser.damage[damage_start].y;
1060 element = Feld[elx][ely];
1063 if (IS_BEAMER(element))
1067 for (i = 0; i < laser.num_beamers; i++)
1068 printf("-> %d\n", laser.beamer_edge[i]);
1069 printf("DrawLaserExt: IS_BEAMER: [%d]: Hit[%d][%d] == %d [%d]\n",
1070 mode, elx, ely, Hit[elx][ely], start_edge);
1071 printf("DrawLaserExt: IS_BEAMER: %d / %d\n",
1072 get_element_angle(element), laser.damage[damage_start].angle);
1076 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1077 laser.num_beamers > 0 &&
1078 start_edge == laser.beamer_edge[laser.num_beamers - 1])
1080 /* element is outgoing beamer */
1081 laser.num_damages = damage_start + 1;
1083 if (IS_BEAMER(element))
1084 laser.current_angle = get_element_angle(element);
1088 /* element is incoming beamer or other element */
1089 laser.num_damages = damage_start;
1090 laser.current_angle = laser.damage[laser.num_damages].angle;
1095 /* no damages but McDuffin himself (who needs to be redrawn anyway) */
1097 elx = laser.start_edge.x;
1098 ely = laser.start_edge.y;
1099 element = Feld[elx][ely];
1102 laser.num_edges = start_edge + 1;
1103 if (start_edge == 0)
1104 laser.current_angle = laser.start_angle;
1106 LX = laser.edge[start_edge].x - (SX + 2);
1107 LY = laser.edge[start_edge].y - (SY + 2);
1108 XS = 2 * Step[laser.current_angle].x;
1109 YS = 2 * Step[laser.current_angle].y;
1112 printf("DrawLaser: Set (LX, LY) to (%d, %d) [%d]\n",
1118 if (IS_BEAMER(element) ||
1119 IS_FIBRE_OPTIC(element) ||
1120 IS_PACMAN(element) ||
1121 IS_POLAR(element) ||
1122 IS_POLAR_CROSS(element) ||
1123 element == EL_FUSE_ON)
1128 printf("element == %d\n", element);
1131 if (IS_22_5_ANGLE(laser.current_angle)) /* neither 90° nor 45° angle */
1132 step_size = ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) ? 4 : 3);
1136 if (IS_POLAR(element) || IS_POLAR_CROSS(element) ||
1137 ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1138 (laser.num_beamers == 0 ||
1139 start_edge != laser.beamer_edge[laser.num_beamers - 1])))
1141 /* element is incoming beamer or other element */
1142 step_size = -step_size;
1147 if (IS_BEAMER(element))
1149 printf("start_edge == %d, laser.beamer_edge == %d\n",
1150 start_edge, laser.beamer_edge);
1154 LX += step_size * XS;
1155 LY += step_size * YS;
1157 else if (element != EL_EMPTY)
1166 printf("DrawLaser: Finally: (LX, LY) to (%d, %d) [%d]\n",
1171 void DrawLaser(int start_edge, int mode)
1173 if (laser.num_edges - start_edge < 0)
1175 Error(ERR_WARN, "DrawLaser: laser.num_edges - start_edge < 0");
1180 /* check if laser is interrupted by beamer element */
1181 if (laser.num_beamers > 0 &&
1182 start_edge < laser.beamer_edge[laser.num_beamers - 1])
1184 if (mode == DL_LASER_ENABLED)
1187 int tmp_start_edge = start_edge;
1189 /* draw laser segments forward from the start to the last beamer */
1190 for (i = 0; i < laser.num_beamers; i++)
1192 int tmp_num_edges = laser.beamer_edge[i] - tmp_start_edge;
1194 if (tmp_num_edges <= 0)
1198 printf("DrawLaser: DL_LASER_ENABLED: i==%d: %d, %d\n",
1199 i, laser.beamer_edge[i], tmp_start_edge);
1202 DrawLaserExt(tmp_start_edge, tmp_num_edges, DL_LASER_ENABLED);
1204 tmp_start_edge = laser.beamer_edge[i];
1207 /* draw last segment from last beamer to the end */
1208 DrawLaserExt(tmp_start_edge, laser.num_edges - tmp_start_edge,
1214 int last_num_edges = laser.num_edges;
1215 int num_beamers = laser.num_beamers;
1217 /* delete laser segments backward from the end to the first beamer */
1218 for (i = num_beamers-1; i >= 0; i--)
1220 int tmp_num_edges = last_num_edges - laser.beamer_edge[i];
1222 if (laser.beamer_edge[i] - start_edge <= 0)
1225 DrawLaserExt(laser.beamer_edge[i], tmp_num_edges, DL_LASER_DISABLED);
1227 last_num_edges = laser.beamer_edge[i];
1228 laser.num_beamers--;
1232 if (last_num_edges - start_edge <= 0)
1233 printf("DrawLaser: DL_LASER_DISABLED: %d, %d\n",
1234 last_num_edges, start_edge);
1237 /* delete first segment from start to the first beamer */
1238 DrawLaserExt(start_edge, last_num_edges - start_edge, DL_LASER_DISABLED);
1243 DrawLaserExt(start_edge, laser.num_edges - start_edge, mode);
1247 boolean HitElement(int element, int hit_mask)
1249 if (HitOnlyAnEdge(element, hit_mask))
1252 if (IS_MOVING(ELX, ELY) || IS_BLOCKED(ELX, ELY))
1253 element = MovingOrBlocked2Element_MM(ELX, ELY);
1256 printf("HitElement (1): element == %d\n", element);
1260 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1261 printf("HitElement (%d): EXACT MATCH @ (%d, %d)\n", element, ELX, ELY);
1263 printf("HitElement (%d): FUZZY MATCH @ (%d, %d)\n", element, ELX, ELY);
1266 AddDamagedField(ELX, ELY);
1268 /* this is more precise: check if laser would go through the center */
1269 if ((ELX * TILEX + 14 - LX) * YS != (ELY * TILEY + 14 - LY) * XS)
1271 /* skip the whole element before continuing the scan */
1277 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1279 if (LX/TILEX > ELX || LY/TILEY > ELY)
1281 /* skipping scan positions to the right and down skips one scan
1282 position too much, because this is only the top left scan position
1283 of totally four scan positions (plus one to the right, one to the
1284 bottom and one to the bottom right) */
1294 printf("HitElement (2): element == %d\n", element);
1297 if (LX + 5 * XS < 0 ||
1307 printf("HitElement (3): element == %d\n", element);
1310 if (IS_POLAR(element) &&
1311 ((element - EL_POLAR_START) % 2 ||
1312 (element - EL_POLAR_START) / 2 != laser.current_angle % 8))
1314 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1316 laser.num_damages--;
1321 if (IS_POLAR_CROSS(element) &&
1322 (element - EL_POLAR_CROSS_START) != laser.current_angle % 4)
1324 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1326 laser.num_damages--;
1331 if (!IS_BEAMER(element) &&
1332 !IS_FIBRE_OPTIC(element) &&
1333 !IS_GRID_WOOD(element) &&
1334 element != EL_FUEL_EMPTY)
1337 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1338 printf("EXACT MATCH @ (%d, %d)\n", ELX, ELY);
1340 printf("FUZZY MATCH @ (%d, %d)\n", ELX, ELY);
1343 LX = ELX * TILEX + 14;
1344 LY = ELY * TILEY + 14;
1346 AddLaserEdge(LX, LY);
1349 if (IS_MIRROR(element) ||
1350 IS_MIRROR_FIXED(element) ||
1351 IS_POLAR(element) ||
1352 IS_POLAR_CROSS(element) ||
1353 IS_DF_MIRROR(element) ||
1354 IS_DF_MIRROR_AUTO(element) ||
1355 element == EL_PRISM ||
1356 element == EL_REFRACTOR)
1358 int current_angle = laser.current_angle;
1361 laser.num_damages--;
1363 AddDamagedField(ELX, ELY);
1365 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1368 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1370 if (IS_MIRROR(element) ||
1371 IS_MIRROR_FIXED(element) ||
1372 IS_DF_MIRROR(element) ||
1373 IS_DF_MIRROR_AUTO(element))
1374 laser.current_angle = get_mirrored_angle(laser.current_angle,
1375 get_element_angle(element));
1377 if (element == EL_PRISM || element == EL_REFRACTOR)
1378 laser.current_angle = RND(16);
1380 XS = 2 * Step[laser.current_angle].x;
1381 YS = 2 * Step[laser.current_angle].y;
1383 if (!IS_22_5_ANGLE(laser.current_angle)) /* 90° or 45° angle */
1388 LX += step_size * XS;
1389 LY += step_size * YS;
1392 /* draw sparkles on mirror */
1393 if ((IS_MIRROR(element) || IS_MIRROR_FIXED(element)) &&
1394 current_angle != laser.current_angle)
1396 MoveSprite(vp, &Pfeil[2], 4 + 16 * ELX, 5 + 16 * ELY + 1);
1400 if ((!IS_POLAR(element) && !IS_POLAR_CROSS(element)) &&
1401 current_angle != laser.current_angle)
1402 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1405 (get_opposite_angle(laser.current_angle) ==
1406 laser.damage[laser.num_damages - 1].angle ? TRUE : FALSE);
1408 return (laser.overloaded ? TRUE : FALSE);
1411 if (element == EL_FUEL_FULL)
1413 laser.stops_inside_element = TRUE;
1418 if (element == EL_BOMB || element == EL_MINE)
1420 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1422 if (element == EL_MINE)
1423 laser.overloaded = TRUE;
1426 if (element == EL_KETTLE ||
1427 element == EL_CELL ||
1428 element == EL_KEY ||
1429 element == EL_LIGHTBALL ||
1430 element == EL_PACMAN ||
1433 if (!IS_PACMAN(element))
1436 if (element == EL_PACMAN)
1439 if (element == EL_KETTLE || element == EL_CELL)
1441 if (game_mm.kettles_still_needed > 0)
1442 game_mm.kettles_still_needed--;
1446 if (game_mm.kettles_still_needed == 0)
1448 int exit_element = (element == EL_KETTLE ? EL_EXIT_OPEN : EL_RECEIVER);
1450 static int xy[4][2] =
1458 PlayLevelSound_MM(ELX, ELY, exit_element, MM_ACTION_OPENING);
1460 for (y = 0; y < lev_fieldy; y++)
1462 for (x = 0; x < lev_fieldx; x++)
1464 /* initiate opening animation of exit door */
1465 if (Feld[x][y] == EL_EXIT_CLOSED)
1466 Feld[x][y] = EL_EXIT_OPENING;
1468 /* remove field that blocks receiver */
1469 if (IS_RECEIVER(Feld[x][y]))
1471 int phase = Feld[x][y] - EL_RECEIVER_START;
1472 int blocking_x, blocking_y;
1474 blocking_x = x + xy[phase][0];
1475 blocking_y = y + xy[phase][1];
1477 if (IN_LEV_FIELD(blocking_x, blocking_y))
1479 Feld[blocking_x][blocking_y] = EL_EMPTY;
1481 DrawField_MM(blocking_x, blocking_y);
1487 DrawLaser(0, DL_LASER_ENABLED);
1490 else if (element == EL_KEY)
1494 else if (element == EL_LIGHTBALL)
1498 else if (IS_PACMAN(element))
1500 DeletePacMan(ELX, ELY);
1507 if (element == EL_LIGHTBULB_OFF || element == EL_LIGHTBULB_ON)
1509 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1511 DrawLaser(0, DL_LASER_ENABLED);
1513 if (Feld[ELX][ELY] == EL_LIGHTBULB_OFF)
1515 Feld[ELX][ELY] = EL_LIGHTBULB_ON;
1516 game_mm.lights_still_needed--;
1520 Feld[ELX][ELY] = EL_LIGHTBULB_OFF;
1521 game_mm.lights_still_needed++;
1524 DrawField_MM(ELX, ELY);
1525 DrawLaser(0, DL_LASER_ENABLED);
1530 laser.stops_inside_element = TRUE;
1536 printf("HitElement (4): element == %d\n", element);
1539 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1540 laser.num_beamers < MAX_NUM_BEAMERS &&
1541 laser.beamer[BEAMER_NR(element)][1].num)
1543 int beamer_angle = get_element_angle(element);
1544 int beamer_nr = BEAMER_NR(element);
1548 printf("HitElement (BEAMER): element == %d\n", element);
1551 laser.num_damages--;
1553 if (IS_FIBRE_OPTIC(element) ||
1554 laser.current_angle == get_opposite_angle(beamer_angle))
1558 LX = ELX * TILEX + 14;
1559 LY = ELY * TILEY + 14;
1561 AddLaserEdge(LX, LY);
1562 AddDamagedField(ELX, ELY);
1564 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1567 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1569 pos = (ELX == laser.beamer[beamer_nr][0].x &&
1570 ELY == laser.beamer[beamer_nr][0].y ? 1 : 0);
1571 ELX = laser.beamer[beamer_nr][pos].x;
1572 ELY = laser.beamer[beamer_nr][pos].y;
1573 LX = ELX * TILEX + 14;
1574 LY = ELY * TILEY + 14;
1576 if (IS_BEAMER(element))
1578 laser.current_angle = get_element_angle(Feld[ELX][ELY]);
1579 XS = 2 * Step[laser.current_angle].x;
1580 YS = 2 * Step[laser.current_angle].y;
1583 laser.beamer_edge[laser.num_beamers] = laser.num_edges;
1585 AddLaserEdge(LX, LY);
1586 AddDamagedField(ELX, ELY);
1588 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1591 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1593 if (laser.current_angle == (laser.current_angle >> 1) << 1)
1598 LX += step_size * XS;
1599 LY += step_size * YS;
1601 laser.num_beamers++;
1610 boolean HitOnlyAnEdge(int element, int hit_mask)
1612 /* check if the laser hit only the edge of an element and, if so, go on */
1615 printf("LX, LY, hit_mask == %d, %d, %d\n", LX, LY, hit_mask);
1618 if ((hit_mask == HIT_MASK_TOPLEFT ||
1619 hit_mask == HIT_MASK_TOPRIGHT ||
1620 hit_mask == HIT_MASK_BOTTOMLEFT ||
1621 hit_mask == HIT_MASK_BOTTOMRIGHT) &&
1622 laser.current_angle % 4) /* angle is not 90° */
1626 if (hit_mask == HIT_MASK_TOPLEFT)
1631 else if (hit_mask == HIT_MASK_TOPRIGHT)
1636 else if (hit_mask == HIT_MASK_BOTTOMLEFT)
1641 else /* (hit_mask == HIT_MASK_BOTTOMRIGHT) */
1647 AddDamagedField((LX + 2 * dx) / TILEX, (LY + 2 * dy) / TILEY);
1653 printf("[HitOnlyAnEdge() == TRUE]\n");
1660 printf("[HitOnlyAnEdge() == FALSE]\n");
1666 boolean HitPolarizer(int element, int hit_mask)
1668 if (HitOnlyAnEdge(element, hit_mask))
1671 if (IS_DF_GRID(element))
1673 int grid_angle = get_element_angle(element);
1676 printf("HitPolarizer: angle: grid == %d, laser == %d\n",
1677 grid_angle, laser.current_angle);
1680 AddLaserEdge(LX, LY);
1681 AddDamagedField(ELX, ELY);
1684 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1686 if (laser.current_angle == grid_angle ||
1687 laser.current_angle == get_opposite_angle(grid_angle))
1689 /* skip the whole element before continuing the scan */
1695 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1697 if (LX/TILEX > ELX || LY/TILEY > ELY)
1699 /* skipping scan positions to the right and down skips one scan
1700 position too much, because this is only the top left scan position
1701 of totally four scan positions (plus one to the right, one to the
1702 bottom and one to the bottom right) */
1708 AddLaserEdge(LX, LY);
1714 printf("HitPolarizer: LX, LY == %d, %d [%d, %d] [%d, %d]\n",
1716 LX / TILEX, LY / TILEY,
1717 LX % TILEX, LY % TILEY);
1722 else if (IS_GRID_STEEL_FIXED(element) || IS_GRID_STEEL_AUTO(element))
1724 return HitReflectingWalls(element, hit_mask);
1728 return HitAbsorbingWalls(element, hit_mask);
1731 else if (IS_GRID_STEEL(element))
1733 return HitReflectingWalls(element, hit_mask);
1735 else /* IS_GRID_WOOD */
1737 return HitAbsorbingWalls(element, hit_mask);
1743 boolean HitBlock(int element, int hit_mask)
1745 boolean check = FALSE;
1747 if ((element == EL_GATE_STONE || element == EL_GATE_WOOD) &&
1748 game_mm.num_keys == 0)
1751 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1754 int ex = ELX * TILEX + 14;
1755 int ey = ELY * TILEY + 14;
1759 for (i = 1; i < 32; i++)
1764 if ((x == ex || x == ex + 1) && (y == ey || y == ey + 1))
1769 if (check && (element == EL_BLOCK_WOOD || element == EL_GATE_WOOD))
1770 return HitAbsorbingWalls(element, hit_mask);
1774 AddLaserEdge(LX - XS, LY - YS);
1775 AddDamagedField(ELX, ELY);
1778 Box[ELX][ELY] = laser.num_edges;
1780 return HitReflectingWalls(element, hit_mask);
1783 if (element == EL_GATE_STONE || element == EL_GATE_WOOD)
1785 int xs = XS / 2, ys = YS / 2;
1786 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1787 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1789 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1790 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1792 laser.overloaded = (element == EL_GATE_STONE);
1797 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1798 (hit_mask == HIT_MASK_TOP ||
1799 hit_mask == HIT_MASK_LEFT ||
1800 hit_mask == HIT_MASK_RIGHT ||
1801 hit_mask == HIT_MASK_BOTTOM))
1802 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1803 hit_mask == HIT_MASK_BOTTOM),
1804 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1805 hit_mask == HIT_MASK_RIGHT));
1806 AddLaserEdge(LX, LY);
1812 if (element == EL_GATE_STONE && Box[ELX][ELY])
1814 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
1826 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1828 int xs = XS / 2, ys = YS / 2;
1829 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1830 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1832 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1833 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1835 laser.overloaded = (element == EL_BLOCK_STONE);
1840 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1841 (hit_mask == HIT_MASK_TOP ||
1842 hit_mask == HIT_MASK_LEFT ||
1843 hit_mask == HIT_MASK_RIGHT ||
1844 hit_mask == HIT_MASK_BOTTOM))
1845 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1846 hit_mask == HIT_MASK_BOTTOM),
1847 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1848 hit_mask == HIT_MASK_RIGHT));
1849 AddDamagedField(ELX, ELY);
1851 LX = ELX * TILEX + 14;
1852 LY = ELY * TILEY + 14;
1854 AddLaserEdge(LX, LY);
1856 laser.stops_inside_element = TRUE;
1864 boolean HitLaserSource(int element, int hit_mask)
1866 if (HitOnlyAnEdge(element, hit_mask))
1869 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1871 laser.overloaded = TRUE;
1876 boolean HitLaserDestination(int element, int hit_mask)
1878 if (HitOnlyAnEdge(element, hit_mask))
1881 if (element != EL_EXIT_OPEN &&
1882 !(IS_RECEIVER(element) &&
1883 game_mm.kettles_still_needed == 0 &&
1884 laser.current_angle == get_opposite_angle(get_element_angle(element))))
1886 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1891 if (IS_RECEIVER(element) ||
1892 (IS_22_5_ANGLE(laser.current_angle) &&
1893 (ELX != (LX + 6 * XS) / TILEX ||
1894 ELY != (LY + 6 * YS) / TILEY ||
1903 LX = ELX * TILEX + 14;
1904 LY = ELY * TILEY + 14;
1906 laser.stops_inside_element = TRUE;
1909 AddLaserEdge(LX, LY);
1910 AddDamagedField(ELX, ELY);
1912 if (game_mm.lights_still_needed == 0)
1913 game_mm.level_solved = TRUE;
1918 boolean HitReflectingWalls(int element, int hit_mask)
1920 /* check if laser hits side of a wall with an angle that is not 90° */
1921 if (!IS_90_ANGLE(laser.current_angle) && (hit_mask == HIT_MASK_TOP ||
1922 hit_mask == HIT_MASK_LEFT ||
1923 hit_mask == HIT_MASK_RIGHT ||
1924 hit_mask == HIT_MASK_BOTTOM))
1926 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1931 if (!IS_DF_GRID(element))
1932 AddLaserEdge(LX, LY);
1934 /* check if laser hits wall with an angle of 45° */
1935 if (!IS_22_5_ANGLE(laser.current_angle))
1937 if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1940 laser.current_angle = get_mirrored_angle(laser.current_angle,
1943 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1946 laser.current_angle = get_mirrored_angle(laser.current_angle,
1950 AddLaserEdge(LX, LY);
1952 XS = 2 * Step[laser.current_angle].x;
1953 YS = 2 * Step[laser.current_angle].y;
1957 else if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1959 laser.current_angle = get_mirrored_angle(laser.current_angle,
1964 if (!IS_DF_GRID(element))
1965 AddLaserEdge(LX, LY);
1970 if (!IS_DF_GRID(element))
1971 AddLaserEdge(LX, LY + YS / 2);
1974 if (!IS_DF_GRID(element))
1975 AddLaserEdge(LX, LY);
1978 YS = 2 * Step[laser.current_angle].y;
1982 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1984 laser.current_angle = get_mirrored_angle(laser.current_angle,
1989 if (!IS_DF_GRID(element))
1990 AddLaserEdge(LX, LY);
1995 if (!IS_DF_GRID(element))
1996 AddLaserEdge(LX + XS / 2, LY);
1999 if (!IS_DF_GRID(element))
2000 AddLaserEdge(LX, LY);
2003 XS = 2 * Step[laser.current_angle].x;
2009 /* reflection at the edge of reflecting DF style wall */
2010 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
2012 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
2013 hit_mask == HIT_MASK_TOPRIGHT) ||
2014 ((laser.current_angle == 5 || laser.current_angle == 7) &&
2015 hit_mask == HIT_MASK_TOPLEFT) ||
2016 ((laser.current_angle == 9 || laser.current_angle == 11) &&
2017 hit_mask == HIT_MASK_BOTTOMLEFT) ||
2018 ((laser.current_angle == 13 || laser.current_angle == 15) &&
2019 hit_mask == HIT_MASK_BOTTOMRIGHT))
2022 (hit_mask == HIT_MASK_TOPRIGHT || hit_mask == HIT_MASK_BOTTOMLEFT ?
2023 ANG_MIRROR_135 : ANG_MIRROR_45);
2025 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
2027 AddDamagedField(ELX, ELY);
2028 AddLaserEdge(LX, LY);
2030 laser.current_angle = get_mirrored_angle(laser.current_angle,
2038 AddLaserEdge(LX, LY);
2044 /* reflection inside an edge of reflecting DF style wall */
2045 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
2047 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
2048 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT)) ||
2049 ((laser.current_angle == 5 || laser.current_angle == 7) &&
2050 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMRIGHT)) ||
2051 ((laser.current_angle == 9 || laser.current_angle == 11) &&
2052 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT)) ||
2053 ((laser.current_angle == 13 || laser.current_angle == 15) &&
2054 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPLEFT)))
2057 (hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT) ||
2058 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT) ?
2059 ANG_MIRROR_135 : ANG_MIRROR_45);
2061 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
2064 AddDamagedField(ELX, ELY);
2067 AddLaserEdge(LX - XS, LY - YS);
2068 AddLaserEdge(LX - XS + (ABS(XS) == 4 ? XS/2 : 0),
2069 LY - YS + (ABS(YS) == 4 ? YS/2 : 0));
2071 laser.current_angle = get_mirrored_angle(laser.current_angle,
2079 AddLaserEdge(LX, LY);
2085 /* check if laser hits DF style wall with an angle of 90° */
2086 if (IS_DF_WALL(element) && IS_90_ANGLE(laser.current_angle))
2088 if ((IS_HORIZ_ANGLE(laser.current_angle) &&
2089 (!(hit_mask & HIT_MASK_TOP) || !(hit_mask & HIT_MASK_BOTTOM))) ||
2090 (IS_VERT_ANGLE(laser.current_angle) &&
2091 (!(hit_mask & HIT_MASK_LEFT) || !(hit_mask & HIT_MASK_RIGHT))))
2093 static int last_LX = 0, last_LY = 0, last_hit_mask = 0;
2095 /* laser at last step touched nothing or the same side of the wall */
2096 if (LX != last_LX || LY != last_LY || hit_mask == last_hit_mask)
2098 AddDamagedField(ELX, ELY);
2105 last_hit_mask = hit_mask;
2112 if (!HitOnlyAnEdge(element, hit_mask))
2114 laser.overloaded = TRUE;
2122 boolean HitAbsorbingWalls(int element, int hit_mask)
2124 if (HitOnlyAnEdge(element, hit_mask))
2128 (hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT))
2130 AddLaserEdge(LX - XS, LY - YS);
2137 (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM))
2139 AddLaserEdge(LX - XS, LY - YS);
2145 if (IS_WALL_WOOD(element) ||
2146 IS_DF_WALL_WOOD(element) ||
2147 IS_GRID_WOOD(element) ||
2148 IS_GRID_WOOD_FIXED(element) ||
2149 IS_GRID_WOOD_AUTO(element) ||
2150 element == EL_FUSE_ON ||
2151 element == EL_BLOCK_WOOD ||
2152 element == EL_GATE_WOOD)
2154 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
2159 if (IS_WALL_ICE(element))
2163 mask = (LX + XS) / MINI_TILEX - ELX * 2 + 1; /* Quadrant (horizontal) */
2164 mask <<= (((LY + YS) / MINI_TILEY - ELY * 2) > 0) * 2; /* || (vertical) */
2166 /* check if laser hits wall with an angle of 90° */
2167 if (IS_90_ANGLE(laser.current_angle))
2168 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
2170 if (mask == 1 || mask == 2 || mask == 4 || mask == 8)
2174 for (i = 0; i < 4; i++)
2176 if (mask == (1 << i) && (XS > 0) == (i % 2) && (YS > 0) == (i / 2))
2177 mask = 15 - (8 >> i);
2178 else if (ABS(XS) == 4 &&
2180 (XS > 0) == (i % 2) &&
2181 (YS < 0) == (i / 2))
2182 mask = 3 + (i / 2) * 9;
2183 else if (ABS(YS) == 4 &&
2185 (XS < 0) == (i % 2) &&
2186 (YS > 0) == (i / 2))
2187 mask = 5 + (i % 2) * 5;
2191 laser.wall_mask = mask;
2193 else if (IS_WALL_AMOEBA(element))
2195 int elx = (LX - 2 * XS) / TILEX;
2196 int ely = (LY - 2 * YS) / TILEY;
2197 int element2 = Feld[elx][ely];
2200 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element2))
2202 laser.dest_element = EL_EMPTY;
2210 mask = (LX - 2 * XS) / 16 - ELX * 2 + 1;
2211 mask <<= ((LY - 2 * YS) / 16 - ELY * 2) * 2;
2213 if (IS_90_ANGLE(laser.current_angle))
2214 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
2216 laser.dest_element = element2 | EL_WALL_AMOEBA;
2218 laser.wall_mask = mask;
2224 void OpenExit(int x, int y)
2228 if (!MovDelay[x][y]) /* next animation frame */
2229 MovDelay[x][y] = 4 * delay;
2231 if (MovDelay[x][y]) /* wait some time before next frame */
2236 phase = MovDelay[x][y] / delay;
2238 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2239 DrawGraphicAnimation_MM(x, y, IMG_MM_EXIT_OPENING, 3 - phase);
2241 if (!MovDelay[x][y])
2243 Feld[x][y] = EL_EXIT_OPEN;
2249 void OpenSurpriseBall(int x, int y)
2253 if (!MovDelay[x][y]) /* next animation frame */
2254 MovDelay[x][y] = 50 * delay;
2256 if (MovDelay[x][y]) /* wait some time before next frame */
2260 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2263 int graphic = el2gfx(Store[x][y]);
2265 int dx = RND(26), dy = RND(26);
2267 getGraphicSource(graphic, 0, &bitmap, &gx, &gy);
2269 BlitBitmap(bitmap, drawto, gx + dx, gy + dy, 6, 6,
2270 SX + x * TILEX + dx, SY + y * TILEY + dy);
2272 MarkTileDirty(x, y);
2275 if (!MovDelay[x][y])
2277 Feld[x][y] = Store[x][y];
2286 void MeltIce(int x, int y)
2291 if (!MovDelay[x][y]) /* next animation frame */
2292 MovDelay[x][y] = frames * delay;
2294 if (MovDelay[x][y]) /* wait some time before next frame */
2297 int wall_mask = Store2[x][y];
2298 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_ICE;
2301 phase = frames - MovDelay[x][y] / delay - 1;
2303 if (!MovDelay[x][y])
2307 Feld[x][y] = real_element & (wall_mask ^ 0xFF);
2308 Store[x][y] = Store2[x][y] = 0;
2310 DrawWalls_MM(x, y, Feld[x][y]);
2312 if (Feld[x][y] == EL_WALL_ICE)
2313 Feld[x][y] = EL_EMPTY;
2315 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
2316 if (laser.damage[i].is_mirror)
2320 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
2322 DrawLaser(0, DL_LASER_DISABLED);
2326 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2328 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2330 laser.redraw = TRUE;
2335 void GrowAmoeba(int x, int y)
2340 if (!MovDelay[x][y]) /* next animation frame */
2341 MovDelay[x][y] = frames * delay;
2343 if (MovDelay[x][y]) /* wait some time before next frame */
2346 int wall_mask = Store2[x][y];
2347 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_AMOEBA;
2350 phase = MovDelay[x][y] / delay;
2352 if (!MovDelay[x][y])
2354 Feld[x][y] = real_element;
2355 Store[x][y] = Store2[x][y] = 0;
2357 DrawWalls_MM(x, y, Feld[x][y]);
2358 DrawLaser(0, DL_LASER_ENABLED);
2360 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2362 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2367 static void Explode_MM(int x, int y, int phase, int mode)
2369 int num_phase = 9, delay = 2;
2370 int last_phase = num_phase * delay;
2371 int half_phase = (num_phase / 2) * delay;
2373 laser.redraw = TRUE;
2375 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2377 int center_element = Feld[x][y];
2379 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2381 /* put moving element to center field (and let it explode there) */
2382 center_element = MovingOrBlocked2Element_MM(x, y);
2383 RemoveMovingField_MM(x, y);
2385 Feld[x][y] = center_element;
2388 if (center_element == EL_BOMB || IS_MCDUFFIN(center_element))
2389 Store[x][y] = center_element;
2391 Store[x][y] = EL_EMPTY;
2393 Store2[x][y] = mode;
2394 Feld[x][y] = EL_EXPLODING_OPAQUE;
2395 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2401 Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
2403 if (phase == half_phase)
2405 Feld[x][y] = EL_EXPLODING_TRANSP;
2407 if (x == ELX && y == ELY)
2411 if (phase == last_phase)
2413 if (Store[x][y] == EL_BOMB)
2415 DrawLaser(0, DL_LASER_DISABLED);
2418 Bang_MM(laser.start_edge.x, laser.start_edge.y);
2419 Store[x][y] = EL_EMPTY;
2421 game_mm.game_over = TRUE;
2422 game_mm.game_over_cause = GAME_OVER_BOMB;
2424 laser.overloaded = FALSE;
2426 else if (IS_MCDUFFIN(Store[x][y]))
2428 Store[x][y] = EL_EMPTY;
2431 Feld[x][y] = Store[x][y];
2432 Store[x][y] = Store2[x][y] = 0;
2433 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2435 InitField(x, y, FALSE);
2438 else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2440 int graphic = IMG_MM_DEFAULT_EXPLODING;
2441 int graphic_phase = (phase / delay - 1);
2445 if (Store2[x][y] == EX_KETTLE)
2447 if (graphic_phase < 3)
2449 graphic = IMG_MM_KETTLE_EXPLODING;
2451 else if (graphic_phase < 5)
2457 graphic = IMG_EMPTY;
2461 else if (Store2[x][y] == EX_SHORT)
2463 if (graphic_phase < 4)
2469 graphic = IMG_EMPTY;
2474 getGraphicSource(graphic, graphic_phase, &bitmap, &src_x, &src_y);
2476 BlitBitmap(bitmap, drawto_field, src_x, src_y, TILEX, TILEY,
2477 FX + x * TILEX, FY + y * TILEY);
2479 MarkTileDirty(x, y);
2483 static void Bang_MM(int x, int y)
2485 int element = Feld[x][y];
2486 int mode = EX_NORMAL;
2489 DrawLaser(0, DL_LASER_ENABLED);
2508 if (IS_PACMAN(element))
2509 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2510 else if (element == EL_BOMB || IS_MCDUFFIN(element))
2511 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2512 else if (element == EL_KEY)
2513 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2515 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2517 Explode_MM(x, y, EX_PHASE_START, mode);
2520 void TurnRound(int x, int y)
2532 { 0, 0 }, { 0, 0 }, { 0, 0 },
2537 int left, right, back;
2541 { MV_DOWN, MV_UP, MV_RIGHT },
2542 { MV_UP, MV_DOWN, MV_LEFT },
2544 { MV_LEFT, MV_RIGHT, MV_DOWN },
2545 { 0,0,0 }, { 0,0,0 }, { 0,0,0 },
2546 { MV_RIGHT, MV_LEFT, MV_UP }
2549 int element = Feld[x][y];
2550 int old_move_dir = MovDir[x][y];
2551 int right_dir = turn[old_move_dir].right;
2552 int back_dir = turn[old_move_dir].back;
2553 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
2554 int right_x = x + right_dx, right_y = y + right_dy;
2556 if (element == EL_PACMAN)
2558 boolean can_turn_right = FALSE;
2560 if (IN_LEV_FIELD(right_x, right_y) &&
2561 IS_EATABLE4PACMAN(Feld[right_x][right_y]))
2562 can_turn_right = TRUE;
2565 MovDir[x][y] = right_dir;
2567 MovDir[x][y] = back_dir;
2573 static void StartMoving_MM(int x, int y)
2575 int element = Feld[x][y];
2580 if (CAN_MOVE(element))
2584 if (MovDelay[x][y]) /* wait some time before next movement */
2592 /* now make next step */
2594 Moving2Blocked_MM(x, y, &newx, &newy); /* get next screen position */
2596 if (element == EL_PACMAN &&
2597 IN_LEV_FIELD(newx, newy) && IS_EATABLE4PACMAN(Feld[newx][newy]) &&
2598 !ObjHit(newx, newy, HIT_POS_CENTER))
2600 Store[newx][newy] = Feld[newx][newy];
2601 Feld[newx][newy] = EL_EMPTY;
2603 DrawField_MM(newx, newy);
2605 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy) ||
2606 ObjHit(newx, newy, HIT_POS_CENTER))
2608 /* object was running against a wall */
2615 InitMovingField_MM(x, y, MovDir[x][y]);
2619 ContinueMoving_MM(x, y);
2622 static void ContinueMoving_MM(int x, int y)
2624 int element = Feld[x][y];
2625 int direction = MovDir[x][y];
2626 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2627 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2628 int horiz_move = (dx!=0);
2629 int newx = x + dx, newy = y + dy;
2630 int step = (horiz_move ? dx : dy) * TILEX / 8;
2632 MovPos[x][y] += step;
2634 if (ABS(MovPos[x][y]) >= TILEX) /* object reached its destination */
2636 Feld[x][y] = EL_EMPTY;
2637 Feld[newx][newy] = element;
2639 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2640 MovDelay[newx][newy] = 0;
2642 if (!CAN_MOVE(element))
2643 MovDir[newx][newy] = 0;
2646 DrawField_MM(newx, newy);
2648 Stop[newx][newy] = TRUE;
2650 if (element == EL_PACMAN)
2652 if (Store[newx][newy] == EL_BOMB)
2653 Bang_MM(newx, newy);
2655 if (IS_WALL_AMOEBA(Store[newx][newy]) &&
2656 (LX + 2 * XS) / TILEX == newx &&
2657 (LY + 2 * YS) / TILEY == newy)
2664 else /* still moving on */
2669 laser.redraw = TRUE;
2672 void ClickElement(int x, int y, int button)
2674 static unsigned int click_delay = 0;
2675 static int click_delay_value = CLICK_DELAY;
2676 static boolean new_button = TRUE;
2679 /* do not rotate objects hit by the laser after the game was solved */
2680 if (game_mm.level_solved && Hit[x][y])
2683 if (button == MB_RELEASED)
2686 click_delay_value = CLICK_DELAY;
2688 /* release eventually hold auto-rotating mirror */
2689 RotateMirror(x, y, MB_RELEASED);
2694 if (!FrameReached(&click_delay, click_delay_value) && !new_button)
2697 if (button == MB_MIDDLEBUTTON) /* middle button has no function */
2700 if (!IN_LEV_FIELD(x, y))
2703 if (Feld[x][y] == EL_EMPTY)
2706 element = Feld[x][y];
2708 if (IS_MIRROR(element) ||
2709 IS_BEAMER(element) ||
2710 IS_POLAR(element) ||
2711 IS_POLAR_CROSS(element) ||
2712 IS_DF_MIRROR(element) ||
2713 IS_DF_MIRROR_AUTO(element))
2715 RotateMirror(x, y, button);
2717 else if (IS_MCDUFFIN(element))
2719 if (!laser.fuse_off)
2721 DrawLaser(0, DL_LASER_DISABLED);
2728 element = get_rotated_element(element, BUTTON_ROTATION(button));
2729 laser.start_angle = get_element_angle(element);
2733 Feld[x][y] = element;
2740 if (!laser.fuse_off)
2743 else if (element == EL_FUSE_ON && laser.fuse_off)
2745 if (x != laser.fuse_x || y != laser.fuse_y)
2748 laser.fuse_off = FALSE;
2749 laser.fuse_x = laser.fuse_y = -1;
2751 DrawGraphic_MM(x, y, IMG_MM_FUSE_ACTIVE);
2754 else if (element == EL_FUSE_ON && !laser.fuse_off && new_button)
2756 laser.fuse_off = TRUE;
2759 laser.overloaded = FALSE;
2761 DrawLaser(0, DL_LASER_DISABLED);
2762 DrawGraphic_MM(x, y, IMG_MM_FUSE);
2764 else if (element == EL_LIGHTBALL)
2768 DrawLaser(0, DL_LASER_ENABLED);
2771 click_delay_value = (new_button ? CLICK_DELAY_FIRST : CLICK_DELAY);
2775 void RotateMirror(int x, int y, int button)
2777 static int hold_x = -1, hold_y = -1;
2779 if (button == MB_RELEASED)
2781 /* release eventually hold auto-rotating mirror */
2788 if (IS_MIRROR(Feld[x][y]) ||
2789 IS_POLAR_CROSS(Feld[x][y]) ||
2790 IS_POLAR(Feld[x][y]) ||
2791 IS_BEAMER(Feld[x][y]) ||
2792 IS_DF_MIRROR(Feld[x][y]) ||
2793 IS_GRID_STEEL_AUTO(Feld[x][y]) ||
2794 IS_GRID_WOOD_AUTO(Feld[x][y]))
2796 Feld[x][y] = get_rotated_element(Feld[x][y], BUTTON_ROTATION(button));
2798 else if (IS_DF_MIRROR_AUTO(Feld[x][y]))
2800 if (button == MB_LEFTBUTTON)
2802 /* left mouse button only for manual adjustment, no auto-rotating;
2803 freeze mirror for until mouse button released */
2807 else if (button == MB_RIGHTBUTTON && (hold_x != x || hold_y != y))
2809 Feld[x][y] = get_rotated_element(Feld[x][y], ROTATE_RIGHT);
2813 if (IS_GRID_STEEL_AUTO(Feld[x][y]) || IS_GRID_WOOD_AUTO(Feld[x][y]))
2815 int edge = Hit[x][y];
2821 DrawLaser(edge - 1, DL_LASER_DISABLED);
2825 else if (ObjHit(x, y, HIT_POS_CENTER))
2827 int edge = Hit[x][y];
2831 Error(ERR_WARN, "RotateMirror: inconsistent field Hit[][]!\n");
2835 DrawLaser(edge - 1, DL_LASER_DISABLED);
2842 if (ObjHit(x, y, HIT_POS_EDGE | HIT_POS_BETWEEN))
2847 if ((IS_BEAMER(Feld[x][y]) ||
2848 IS_POLAR(Feld[x][y]) ||
2849 IS_POLAR_CROSS(Feld[x][y])) && x == ELX && y == ELY)
2853 if (IS_BEAMER(Feld[x][y]))
2856 printf("TEST (%d, %d) [%d] [%d]\n",
2858 laser.beamer_edge, laser.beamer[1].num);
2868 DrawLaser(0, DL_LASER_ENABLED);
2872 void AutoRotateMirrors()
2874 static unsigned int rotate_delay = 0;
2877 if (!FrameReached(&rotate_delay, AUTO_ROTATE_DELAY))
2880 for (x = 0; x < lev_fieldx; x++)
2882 for (y = 0; y < lev_fieldy; y++)
2884 int element = Feld[x][y];
2886 /* do not rotate objects hit by the laser after the game was solved */
2887 if (game_mm.level_solved && Hit[x][y])
2890 if (IS_DF_MIRROR_AUTO(element) ||
2891 IS_GRID_WOOD_AUTO(element) ||
2892 IS_GRID_STEEL_AUTO(element) ||
2893 element == EL_REFRACTOR)
2894 RotateMirror(x, y, MB_RIGHTBUTTON);
2899 boolean ObjHit(int obx, int oby, int bits)
2906 if (bits & HIT_POS_CENTER)
2908 if (ReadPixel(drawto, SX + obx + 15, SY + oby + 15) == pen_ray)
2912 if (bits & HIT_POS_EDGE)
2914 for (i = 0; i < 4; i++)
2915 if (ReadPixel(drawto,
2916 SX + obx + 31 * (i % 2),
2917 SY + oby + 31 * (i / 2)) == pen_ray)
2921 if (bits & HIT_POS_BETWEEN)
2923 for (i = 0; i < 4; i++)
2924 if (ReadPixel(drawto,
2925 SX + 4 + obx + 22 * (i % 2),
2926 SY + 4 + oby + 22 * (i / 2)) == pen_ray)
2933 void DeletePacMan(int px, int py)
2939 if (game_mm.num_pacman <= 1)
2941 game_mm.num_pacman = 0;
2945 for (i = 0; i < game_mm.num_pacman; i++)
2946 if (game_mm.pacman[i].x == px && game_mm.pacman[i].y == py)
2949 game_mm.num_pacman--;
2951 for (j = i; j < game_mm.num_pacman; j++)
2953 game_mm.pacman[j].x = game_mm.pacman[j + 1].x;
2954 game_mm.pacman[j].y = game_mm.pacman[j + 1].y;
2955 game_mm.pacman[j].dir = game_mm.pacman[j + 1].dir;
2959 void ColorCycling(void)
2961 static int CC, Cc = 0;
2963 static int color, old = 0xF00, new = 0x010, mult = 1;
2964 static unsigned short red, green, blue;
2966 if (color_status == STATIC_COLORS)
2971 if (CC < Cc || CC > Cc + 2)
2975 color = old + new * mult;
2981 if (ABS(mult) == 16)
2991 red = 0x0e00 * ((color & 0xF00) >> 8);
2992 green = 0x0e00 * ((color & 0x0F0) >> 4);
2993 blue = 0x0e00 * ((color & 0x00F));
2994 SetRGB(pen_magicolor[0], red, green, blue);
2996 red = 0x1111 * ((color & 0xF00) >> 8);
2997 green = 0x1111 * ((color & 0x0F0) >> 4);
2998 blue = 0x1111 * ((color & 0x00F));
2999 SetRGB(pen_magicolor[1], red, green, blue);
3003 static void GameActions_MM_Ext(struct MouseActionInfo action, boolean warp_mode)
3005 static unsigned int pacman_delay = 0;
3006 static unsigned int energy_delay = 0;
3007 static unsigned int overload_delay = 0;
3013 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3016 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3018 element = Feld[x][y];
3020 if (!IS_MOVING(x, y) && CAN_MOVE(element))
3021 StartMoving_MM(x, y);
3022 else if (IS_MOVING(x, y))
3023 ContinueMoving_MM(x, y);
3024 else if (IS_EXPLODING(element))
3025 Explode_MM(x, y, Frame[x][y], EX_NORMAL);
3026 else if (element == EL_EXIT_OPENING)
3028 else if (element == EL_GRAY_BALL_OPENING)
3029 OpenSurpriseBall(x, y);
3030 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_ICE)
3032 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_AMOEBA)
3036 AutoRotateMirrors();
3039 /* !!! CHANGE THIS: REDRAW ONLY WHEN NEEDED !!! */
3041 /* redraw after Explode_MM() ... */
3043 DrawLaser(0, DL_LASER_ENABLED);
3044 laser.redraw = FALSE;
3049 if (game_mm.num_pacman && FrameReached(&pacman_delay, PACMAN_MOVE_DELAY))
3053 if (laser.num_damages > MAX_LASER_LEN && !laser.fuse_off)
3055 DrawLaser(0, DL_LASER_DISABLED);
3060 if (FrameReached(&energy_delay, ENERGY_DELAY))
3062 game_mm.energy_left--;
3063 if (game_mm.energy_left >= 0)
3066 BlitBitmap(pix[PIX_DOOR], drawto,
3067 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
3068 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
3069 DX_ENERGY, DY_ENERGY);
3071 redraw_mask |= REDRAW_DOOR_1;
3073 else if (setup.time_limit)
3077 for (i = 15; i >= 0; i--)
3080 SetRGB(pen_ray, 0x0000, 0x0000, i * color_scale);
3082 pen_ray = GetPixelFromRGB(window,
3083 native_mm_level.laser_red * 0x11 * i,
3084 native_mm_level.laser_green * 0x11 * i,
3085 native_mm_level.laser_blue * 0x11 * i);
3087 DrawLaser(0, DL_LASER_ENABLED);
3092 StopSound_MM(SND_MM_GAME_HEALTH_CHARGING);
3095 DrawLaser(0, DL_LASER_DISABLED);
3096 game_mm.game_over = TRUE;
3097 game_mm.game_over_cause = GAME_OVER_NO_ENERGY;
3100 if (Request("Out of magic energy ! Play it again ?",
3101 REQ_ASK | REQ_STAY_CLOSED))
3107 game_status = MAINMENU;
3116 element = laser.dest_element;
3119 if (element != Feld[ELX][ELY])
3121 printf("element == %d, Feld[ELX][ELY] == %d\n",
3122 element, Feld[ELX][ELY]);
3126 if (!laser.overloaded && laser.overload_value == 0 &&
3127 element != EL_BOMB &&
3128 element != EL_MINE &&
3129 element != EL_BALL_GRAY &&
3130 element != EL_BLOCK_STONE &&
3131 element != EL_BLOCK_WOOD &&
3132 element != EL_FUSE_ON &&
3133 element != EL_FUEL_FULL &&
3134 !IS_WALL_ICE(element) &&
3135 !IS_WALL_AMOEBA(element))
3138 if (((laser.overloaded && laser.overload_value < MAX_LASER_OVERLOAD) ||
3139 (!laser.overloaded && laser.overload_value > 0)) &&
3140 FrameReached(&overload_delay, HEALTH_DELAY(laser.overloaded)))
3142 if (laser.overloaded)
3143 laser.overload_value++;
3145 laser.overload_value--;
3147 if (game_mm.cheat_no_overload)
3149 laser.overloaded = FALSE;
3150 laser.overload_value = 0;
3153 game_mm.laser_overload_value = laser.overload_value;
3155 if (laser.overload_value < MAX_LASER_OVERLOAD - 8)
3157 int color_up = 0xFF * laser.overload_value / MAX_LASER_OVERLOAD;
3158 int color_down = 0xFF - color_up;
3161 SetRGB(pen_ray, (laser.overload_value / 6) * color_scale, 0x0000,
3162 (15 - (laser.overload_value / 6)) * color_scale);
3165 GetPixelFromRGB(window,
3166 (native_mm_level.laser_red ? 0xFF : color_up),
3167 (native_mm_level.laser_green ? color_down : 0x00),
3168 (native_mm_level.laser_blue ? color_down : 0x00));
3170 DrawLaser(0, DL_LASER_ENABLED);
3176 if (!laser.overloaded)
3177 StopSound_MM(SND_MM_GAME_HEALTH_CHARGING);
3178 else if (setup.sound_loops)
3179 PlaySoundLoop_MM(SND_MM_GAME_HEALTH_CHARGING);
3181 PlaySound_MM(SND_MM_GAME_HEALTH_CHARGING);
3183 if (laser.overloaded)
3186 BlitBitmap(pix[PIX_DOOR], drawto,
3187 DOOR_GFX_PAGEX4 + XX_OVERLOAD,
3188 DOOR_GFX_PAGEY1 + YY_OVERLOAD + OVERLOAD_YSIZE
3189 - laser.overload_value,
3190 OVERLOAD_XSIZE, laser.overload_value,
3191 DX_OVERLOAD, DY_OVERLOAD + OVERLOAD_YSIZE
3192 - laser.overload_value);
3194 redraw_mask |= REDRAW_DOOR_1;
3199 BlitBitmap(pix[PIX_DOOR], drawto,
3200 DOOR_GFX_PAGEX5 + XX_OVERLOAD, DOOR_GFX_PAGEY1 + YY_OVERLOAD,
3201 OVERLOAD_XSIZE, OVERLOAD_YSIZE - laser.overload_value,
3202 DX_OVERLOAD, DY_OVERLOAD);
3204 redraw_mask |= REDRAW_DOOR_1;
3207 if (laser.overload_value == MAX_LASER_OVERLOAD)
3211 for (i = 15; i >= 0; i--)
3214 SetRGB(pen_ray, i * color_scale, 0x0000, 0x0000);
3217 pen_ray = GetPixelFromRGB(window, 0x11 * i, 0x00, 0x00);
3219 DrawLaser(0, DL_LASER_ENABLED);
3224 DrawLaser(0, DL_LASER_DISABLED);
3226 game_mm.game_over = TRUE;
3227 game_mm.game_over_cause = GAME_OVER_OVERLOADED;
3230 if (Request("Magic spell hit Mc Duffin ! Play it again ?",
3231 REQ_ASK | REQ_STAY_CLOSED))
3237 game_status = MAINMENU;
3251 if (element == EL_BOMB && CT > 75)
3253 if (game_mm.cheat_no_explosion)
3257 laser.num_damages--;
3258 DrawLaser(0, DL_LASER_DISABLED);
3259 laser.num_edges = 0;
3264 laser.dest_element = EL_EXPLODING_OPAQUE;
3268 laser.num_damages--;
3269 DrawLaser(0, DL_LASER_DISABLED);
3271 laser.num_edges = 0;
3272 Bang_MM(laser.start_edge.x, laser.start_edge.y);
3274 if (Request("Bomb killed Mc Duffin ! Play it again ?",
3275 REQ_ASK | REQ_STAY_CLOSED))
3281 game_status = MAINMENU;
3289 if (element == EL_FUSE_ON && CT > 25)
3291 laser.fuse_off = TRUE;
3295 DrawLaser(0, DL_LASER_DISABLED);
3296 DrawGraphic_MM(ELX, ELY, IMG_MM_FUSE);
3299 if (element == EL_BALL_GRAY && CT > 75)
3301 static int new_elements[] =
3304 EL_MIRROR_FIXED_START,
3306 EL_POLAR_CROSS_START,
3312 int num_new_elements = sizeof(new_elements) / sizeof(int);
3313 int new_element = new_elements[RND(num_new_elements)];
3315 Store[ELX][ELY] = new_element + RND(get_num_elements(new_element));
3316 Feld[ELX][ELY] = EL_GRAY_BALL_OPENING;
3318 /* !!! CHECK AGAIN: Laser on Polarizer !!! */
3329 element = EL_MIRROR_START + RND(16);
3335 element = (rnd == 0 ? EL_KETTLE : rnd == 1 ? EL_BOMB : EL_PRISM);
3342 element = (rnd == 0 ? EL_FUSE_ON :
3343 rnd >= 1 && rnd <= 4 ? EL_PACMAN_RIGHT + rnd - 1 :
3344 rnd >= 5 && rnd <= 20 ? EL_POLAR_START + rnd - 5 :
3345 rnd >= 21 && rnd <= 24 ? EL_POLAR_CROSS_START + rnd - 21 :
3346 EL_MIRROR_FIXED_START + rnd - 25);
3351 graphic = el2gfx(element);
3353 for (i = 0; i < 50; i++)
3359 BlitBitmap(pix[PIX_BACK], drawto,
3360 SX + (graphic % GFX_PER_LINE) * TILEX + x,
3361 SY + (graphic / GFX_PER_LINE) * TILEY + y, 6, 6,
3362 SX + ELX * TILEX + x,
3363 SY + ELY * TILEY + y);
3365 MarkTileDirty(ELX, ELY);
3368 DrawLaser(0, DL_LASER_ENABLED);
3373 Feld[ELX][ELY] = element;
3374 DrawField_MM(ELX, ELY);
3377 printf("NEW ELEMENT: (%d, %d)\n", ELX, ELY);
3380 /* above stuff: GRAY BALL -> PRISM !!! */
3382 LX = ELX * TILEX + 14;
3383 LY = ELY * TILEY + 14;
3384 if (laser.current_angle == (laser.current_angle >> 1) << 1)
3391 laser.num_edges -= 2;
3392 laser.num_damages--;
3396 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
3397 if (laser.damage[i].is_mirror)
3401 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3403 DrawLaser(0, DL_LASER_DISABLED);
3405 DrawLaser(0, DL_LASER_DISABLED);
3411 printf("TEST ELEMENT: %d\n", Feld[0][0]);
3418 if (IS_WALL_ICE(element) && CT > 50)
3420 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_SHRINKING);
3423 Feld[ELX][ELY] = Feld[ELX][ELY] - EL_WALL_ICE + EL_WALL_CHANGING;
3424 Store[ELX][ELY] = EL_WALL_ICE;
3425 Store2[ELX][ELY] = laser.wall_mask;
3427 laser.dest_element = Feld[ELX][ELY];
3432 for (i = 0; i < 5; i++)
3438 Feld[ELX][ELY] &= (laser.wall_mask ^ 0xFF);
3442 DrawWallsAnimation_MM(ELX, ELY, Feld[ELX][ELY], phase, laser.wall_mask);
3447 if (Feld[ELX][ELY] == EL_WALL_ICE)
3448 Feld[ELX][ELY] = EL_EMPTY;
3452 LX = laser.edge[laser.num_edges].x - (SX + 2);
3453 LY = laser.edge[laser.num_edges].y - (SY + 2);
3456 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
3457 if (laser.damage[i].is_mirror)
3461 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3463 DrawLaser(0, DL_LASER_DISABLED);
3470 if (IS_WALL_AMOEBA(element) && CT > 60)
3472 int k1, k2, k3, dx, dy, de, dm;
3473 int element2 = Feld[ELX][ELY];
3475 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element))
3478 for (i = laser.num_damages - 1; i >= 0; i--)
3479 if (laser.damage[i].is_mirror)
3482 r = laser.num_edges;
3483 d = laser.num_damages;
3490 DrawLaser(laser.damage[k1].edge - 1, DL_LASER_DISABLED);
3493 DrawLaser(0, DL_LASER_ENABLED);
3496 x = laser.damage[k1].x;
3497 y = laser.damage[k1].y;
3502 for (i = 0; i < 4; i++)
3504 if (laser.wall_mask & (1 << i))
3506 if (ReadPixel(drawto,
3507 SX + ELX * TILEX + 14 + (i % 2) * 2,
3508 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3510 if (ReadPixel(drawto,
3511 SX + ELX * TILEX + 31 * (i % 2),
3512 SY + ELY * TILEY + 14 + (i / 2) * 2) == pen_ray)
3519 for (i = 0; i < 4; i++)
3521 if (laser.wall_mask & (1 << i))
3523 if (ReadPixel(drawto,
3524 SX + ELX * TILEX + 31 * (i % 2),
3525 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3532 if (laser.num_beamers > 0 ||
3533 k1 < 1 || k2 < 4 || k3 < 4 ||
3534 ReadPixel(drawto, SX + ELX * TILEX + 14, SY + ELY * TILEY + 14)
3537 laser.num_edges = r;
3538 laser.num_damages = d;
3540 DrawLaser(0, DL_LASER_DISABLED);
3543 Feld[ELX][ELY] = element | laser.wall_mask;
3547 de = Feld[ELX][ELY];
3548 dm = laser.wall_mask;
3552 int x = ELX, y = ELY;
3553 int wall_mask = laser.wall_mask;
3556 DrawLaser(0, DL_LASER_ENABLED);
3558 PlayLevelSound_MM(dx, dy, element, MM_ACTION_GROWING);
3560 Feld[x][y] = Feld[x][y] - EL_WALL_AMOEBA + EL_WALL_CHANGING;
3561 Store[x][y] = EL_WALL_AMOEBA;
3562 Store2[x][y] = wall_mask;
3568 DrawWallsAnimation_MM(dx, dy, de, 4, dm);
3570 DrawLaser(0, DL_LASER_ENABLED);
3572 PlayLevelSound_MM(dx, dy, element, MM_ACTION_GROWING);
3574 for (i = 4; i >= 0; i--)
3576 DrawWallsAnimation_MM(dx, dy, de, i, dm);
3582 DrawLaser(0, DL_LASER_ENABLED);
3587 if ((element == EL_BLOCK_WOOD || element == EL_BLOCK_STONE) &&
3588 laser.stops_inside_element && CT > 75)
3593 if (ABS(XS) > ABS(YS))
3600 for (i = 0; i < 4; i++)
3607 x = ELX + Step[k * 4].x;
3608 y = ELY + Step[k * 4].y;
3610 if (!IN_LEV_FIELD(x, y) || Feld[x][y] != EL_EMPTY)
3613 if (ObjHit(x, y, HIT_POS_CENTER | HIT_POS_EDGE | HIT_POS_BETWEEN))
3621 laser.overloaded = (element == EL_BLOCK_STONE);
3626 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_PUSHING);
3629 Feld[x][y] = element;
3631 DrawGraphic_MM(ELX, ELY, IMG_EMPTY);
3634 if (element == EL_BLOCK_STONE && Box[ELX][ELY])
3636 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
3637 DrawLaser(laser.num_edges - 1, DL_LASER_ENABLED);
3645 if (element == EL_FUEL_FULL && CT > 10)
3647 for (i = game_mm.energy_left; i <= MAX_LASER_ENERGY; i+=2)
3650 BlitBitmap(pix[PIX_DOOR], drawto,
3651 DOOR_GFX_PAGEX4 + XX_ENERGY,
3652 DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
3653 ENERGY_XSIZE, i, DX_ENERGY,
3654 DY_ENERGY + ENERGY_YSIZE - i);
3657 redraw_mask |= REDRAW_DOOR_1;
3663 game_mm.energy_left = MAX_LASER_ENERGY;
3664 Feld[ELX][ELY] = EL_FUEL_EMPTY;
3665 DrawField_MM(ELX, ELY);
3667 DrawLaser(0, DL_LASER_ENABLED);
3675 void GameActions_MM(struct MouseActionInfo action, boolean warp_mode)
3677 ClickElement(action.lx, action.ly, action.button);
3679 GameActions_MM_Ext(action, warp_mode);
3685 int mx, my, ox, oy, nx, ny;
3689 if (++p >= game_mm.num_pacman)
3692 game_mm.pacman[p].dir--;
3694 for (l = 1; l < 5; l++)
3696 game_mm.pacman[p].dir++;
3698 if (game_mm.pacman[p].dir > 4)
3699 game_mm.pacman[p].dir = 1;
3701 if (game_mm.pacman[p].dir % 2)
3704 my = game_mm.pacman[p].dir - 2;
3709 mx = 3 - game_mm.pacman[p].dir;
3712 ox = game_mm.pacman[p].x;
3713 oy = game_mm.pacman[p].y;
3716 element = Feld[nx][ny];
3718 if (nx < 0 || nx > 15 || ny < 0 || ny > 11)
3721 if (!IS_EATABLE4PACMAN(element))
3724 if (ObjHit(nx, ny, HIT_POS_CENTER))
3727 Feld[ox][oy] = EL_EMPTY;
3729 EL_PACMAN_RIGHT - 1 +
3730 (game_mm.pacman[p].dir - 1 +
3731 (game_mm.pacman[p].dir % 2) * 2);
3733 game_mm.pacman[p].x = nx;
3734 game_mm.pacman[p].y = ny;
3736 DrawGraphic_MM(ox, oy, IMG_EMPTY);
3738 if (element != EL_EMPTY)
3740 int graphic = el2gfx(Feld[nx][ny]);
3745 getGraphicSource(graphic, 0, &bitmap, &src_x, &src_y);
3748 ox = SX + ox * TILEX;
3749 oy = SY + oy * TILEY;
3751 for (i = 1; i < 33; i += 2)
3752 BlitBitmap(bitmap, window,
3753 src_x, src_y, TILEX, TILEY,
3754 ox + i * mx, oy + i * my);
3755 Ct = Ct + FrameCounter - CT;
3758 DrawField_MM(nx, ny);
3761 if (!laser.fuse_off)
3763 DrawLaser(0, DL_LASER_ENABLED);
3765 if (ObjHit(nx, ny, HIT_POS_BETWEEN))
3767 AddDamagedField(nx, ny);
3769 laser.damage[laser.num_damages - 1].edge = 0;
3773 if (element == EL_BOMB)
3774 DeletePacMan(nx, ny);
3776 if (IS_WALL_AMOEBA(element) &&
3777 (LX + 2 * XS) / TILEX == nx &&
3778 (LY + 2 * YS) / TILEY == ny)
3791 boolean raise_level = FALSE;
3794 if (local_player->MovPos)
3797 local_player->LevelSolved = FALSE;
3800 if (game_mm.energy_left)
3802 if (setup.sound_loops)
3803 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3804 SND_CTRL_PLAY_LOOP);
3806 while (game_mm.energy_left > 0)
3808 if (!setup.sound_loops)
3809 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3812 if (game_mm.energy_left > 0 && !(game_mm.energy_left % 10))
3813 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3818 game_mm.energy_left--;
3819 if (game_mm.energy_left >= 0)
3822 BlitBitmap(pix[PIX_DOOR], drawto,
3823 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
3824 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
3825 DX_ENERGY, DY_ENERGY);
3827 redraw_mask |= REDRAW_DOOR_1;
3834 if (setup.sound_loops)
3835 StopSound(SND_SIRR);
3837 else if (native_mm_level.time == 0) /* level without time limit */
3839 if (setup.sound_loops)
3840 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3841 SND_CTRL_PLAY_LOOP);
3843 while (TimePlayed < 999)
3845 if (!setup.sound_loops)
3846 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3847 if (TimePlayed < 999 && !(TimePlayed % 10))
3848 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3849 if (TimePlayed < 900 && !(TimePlayed % 10))
3855 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
3862 if (setup.sound_loops)
3863 StopSound(SND_SIRR);
3870 CloseDoor(DOOR_CLOSE_1);
3872 Request("Level solved !", REQ_CONFIRM);
3874 if (level_nr == leveldir_current->handicap_level)
3876 leveldir_current->handicap_level++;
3877 SaveLevelSetup_SeriesInfo();
3880 if (level_editor_test_game)
3881 game_mm.score = -1; /* no highscore when playing from editor */
3882 else if (level_nr < leveldir_current->last_level)
3883 raise_level = TRUE; /* advance to next level */
3885 if ((hi_pos = NewHiScore_MM()) >= 0)
3887 game_status = HALLOFFAME;
3889 // DrawHallOfFame(hi_pos);
3896 game_status = MAINMENU;
3912 // LoadScore(level_nr);
3914 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
3915 game_mm.score < highscore[MAX_SCORE_ENTRIES - 1].Score)
3918 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3920 if (game_mm.score > highscore[k].Score)
3922 /* player has made it to the hall of fame */
3924 if (k < MAX_SCORE_ENTRIES - 1)
3926 int m = MAX_SCORE_ENTRIES - 1;
3929 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3930 if (!strcmp(setup.player_name, highscore[l].Name))
3932 if (m == k) /* player's new highscore overwrites his old one */
3936 for (l = m; l>k; l--)
3938 strcpy(highscore[l].Name, highscore[l - 1].Name);
3939 highscore[l].Score = highscore[l - 1].Score;
3946 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3947 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3948 highscore[k].Score = game_mm.score;
3955 else if (!strncmp(setup.player_name, highscore[k].Name,
3956 MAX_PLAYER_NAME_LEN))
3957 break; /* player already there with a higher score */
3962 // if (position >= 0)
3963 // SaveScore(level_nr);
3968 static void InitMovingField_MM(int x, int y, int direction)
3970 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3971 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3973 MovDir[x][y] = direction;
3974 MovDir[newx][newy] = direction;
3976 if (Feld[newx][newy] == EL_EMPTY)
3977 Feld[newx][newy] = EL_BLOCKED;
3980 static void Moving2Blocked_MM(int x, int y, int *goes_to_x, int *goes_to_y)
3982 int direction = MovDir[x][y];
3983 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3984 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3990 static void Blocked2Moving_MM(int x, int y,
3991 int *comes_from_x, int *comes_from_y)
3993 int oldx = x, oldy = y;
3994 int direction = MovDir[x][y];
3996 if (direction == MV_LEFT)
3998 else if (direction == MV_RIGHT)
4000 else if (direction == MV_UP)
4002 else if (direction == MV_DOWN)
4005 *comes_from_x = oldx;
4006 *comes_from_y = oldy;
4009 static int MovingOrBlocked2Element_MM(int x, int y)
4011 int element = Feld[x][y];
4013 if (element == EL_BLOCKED)
4017 Blocked2Moving_MM(x, y, &oldx, &oldy);
4019 return Feld[oldx][oldy];
4026 static void RemoveField(int x, int y)
4028 Feld[x][y] = EL_EMPTY;
4035 static void RemoveMovingField_MM(int x, int y)
4037 int oldx = x, oldy = y, newx = x, newy = y;
4039 if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
4042 if (IS_MOVING(x, y))
4044 Moving2Blocked_MM(x, y, &newx, &newy);
4045 if (Feld[newx][newy] != EL_BLOCKED)
4048 else if (Feld[x][y] == EL_BLOCKED)
4050 Blocked2Moving_MM(x, y, &oldx, &oldy);
4051 if (!IS_MOVING(oldx, oldy))
4055 Feld[oldx][oldy] = EL_EMPTY;
4056 Feld[newx][newy] = EL_EMPTY;
4057 MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
4058 MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
4060 DrawLevelField_MM(oldx, oldy);
4061 DrawLevelField_MM(newx, newy);
4064 void PlaySoundLevel(int x, int y, int sound_nr)
4066 int sx = SCREENX(x), sy = SCREENY(y);
4068 int silence_distance = 8;
4070 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
4071 (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
4074 if (!IN_LEV_FIELD(x, y) ||
4075 sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
4076 sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
4079 volume = SOUND_MAX_VOLUME;
4082 stereo = (sx - SCR_FIELDX/2) * 12;
4084 stereo = SOUND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
4085 if (stereo > SOUND_MAX_RIGHT)
4086 stereo = SOUND_MAX_RIGHT;
4087 if (stereo < SOUND_MAX_LEFT)
4088 stereo = SOUND_MAX_LEFT;
4091 if (!IN_SCR_FIELD(sx, sy))
4093 int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
4094 int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
4096 volume -= volume * (dx > dy ? dx : dy) / silence_distance;
4099 PlaySoundExt(sound_nr, volume, stereo, SND_CTRL_PLAY_SOUND);
4102 static void RaiseScore_MM(int value)
4104 game_mm.score += value;
4107 DrawText(DX_SCORE, DY_SCORE, int2str(game_mm.score, 4),
4112 void RaiseScoreElement_MM(int element)
4117 RaiseScore_MM(native_mm_level.score[SC_PACMAN]);
4121 RaiseScore_MM(native_mm_level.score[SC_KEY]);