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 *);
91 /* element masks for scanning pixels of MM elements */
92 static const char mm_masks[10][16][16 + 1] =
276 static int get_element_angle(int element)
278 int element_phase = get_element_phase(element);
280 if (IS_MIRROR_FIXED(element) ||
281 IS_MCDUFFIN(element) ||
283 IS_RECEIVER(element))
284 return 4 * element_phase;
286 return element_phase;
289 static int get_opposite_angle(int angle)
291 int opposite_angle = angle + ANG_RAY_180;
293 /* make sure "opposite_angle" is in valid interval [0, 15] */
294 return (opposite_angle + 16) % 16;
297 static int get_mirrored_angle(int laser_angle, int mirror_angle)
299 int reflected_angle = 16 - laser_angle + mirror_angle;
301 /* make sure "reflected_angle" is in valid interval [0, 15] */
302 return (reflected_angle + 16) % 16;
305 static void InitMovDir_MM(int x, int y)
307 int element = Feld[x][y];
308 static int direction[3][4] =
310 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
311 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
312 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
317 case EL_PACMAN_RIGHT:
321 Feld[x][y] = EL_PACMAN;
322 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
330 static void InitField(int x, int y, boolean init_game)
332 int element = Feld[x][y];
337 Feld[x][y] = EL_EMPTY;
342 if (native_mm_level.auto_count_kettles)
343 game_mm.kettles_still_needed++;
346 case EL_LIGHTBULB_OFF:
347 game_mm.lights_still_needed++;
351 if (IS_MIRROR(element) ||
352 IS_BEAMER_OLD(element) ||
353 IS_BEAMER(element) ||
355 IS_POLAR_CROSS(element) ||
356 IS_DF_MIRROR(element) ||
357 IS_DF_MIRROR_AUTO(element) ||
358 IS_GRID_STEEL_AUTO(element) ||
359 IS_GRID_WOOD_AUTO(element) ||
360 IS_FIBRE_OPTIC(element))
362 if (IS_BEAMER_OLD(element))
364 Feld[x][y] = EL_BEAMER_BLUE_START + (element - EL_BEAMER_START);
365 element = Feld[x][y];
368 if (!IS_FIBRE_OPTIC(element))
370 static int steps_grid_auto = 0;
372 if (game_mm.num_cycle == 0) /* initialize cycle steps for grids */
373 steps_grid_auto = RND(16) * (RND(2) ? -1 : +1);
375 if (IS_GRID_STEEL_AUTO(element) ||
376 IS_GRID_WOOD_AUTO(element))
377 game_mm.cycle[game_mm.num_cycle].steps = steps_grid_auto;
379 game_mm.cycle[game_mm.num_cycle].steps = RND(16) * (RND(2) ? -1 : +1);
381 game_mm.cycle[game_mm.num_cycle].x = x;
382 game_mm.cycle[game_mm.num_cycle].y = y;
386 if (IS_BEAMER(element) || IS_FIBRE_OPTIC(element))
388 int beamer_nr = BEAMER_NR(element);
389 int nr = laser.beamer[beamer_nr][0].num;
391 laser.beamer[beamer_nr][nr].x = x;
392 laser.beamer[beamer_nr][nr].y = y;
393 laser.beamer[beamer_nr][nr].num = 1;
396 else if (IS_PACMAN(element))
400 else if (IS_MCDUFFIN(element) || IS_LASER(element))
402 laser.start_edge.x = x;
403 laser.start_edge.y = y;
404 laser.start_angle = get_element_angle(element);
411 static void InitCycleElements_RotateSingleStep()
415 if (game_mm.num_cycle == 0) /* no elements to cycle */
418 for (i = 0; i < game_mm.num_cycle; i++)
420 int x = game_mm.cycle[i].x;
421 int y = game_mm.cycle[i].y;
422 int step = SIGN(game_mm.cycle[i].steps);
423 int last_element = Feld[x][y];
424 int next_element = get_rotated_element(last_element, step);
426 if (!game_mm.cycle[i].steps)
429 Feld[x][y] = next_element;
432 game_mm.cycle[i].steps -= step;
436 static void InitLaser()
438 int start_element = Feld[laser.start_edge.x][laser.start_edge.y];
439 int step = (IS_LASER(start_element) ? 4 : 0);
441 LX = laser.start_edge.x * TILEX;
442 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
445 LX += (laser.start_angle == ANG_RAY_RIGHT ? 28 + step : 0 - step);
447 LY = laser.start_edge.y * TILEY;
448 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
449 LY += (laser.start_angle == ANG_RAY_DOWN ? 28 + step : 0 - step);
453 XS = 2 * Step[laser.start_angle].x;
454 YS = 2 * Step[laser.start_angle].y;
456 laser.current_angle = laser.start_angle;
458 laser.num_damages = 0;
460 laser.num_beamers = 0;
461 laser.beamer_edge[0] = 0;
463 laser.dest_element = EL_EMPTY;
466 AddLaserEdge(LX, LY); /* set laser starting edge */
468 pen_ray = GetPixelFromRGB(window,
469 native_mm_level.laser_red * 0xFF,
470 native_mm_level.laser_green * 0xFF,
471 native_mm_level.laser_blue * 0xFF);
474 void InitGameEngine_MM()
478 /* set global game control values */
479 game_mm.num_cycle = 0;
480 game_mm.num_pacman = 0;
483 game_mm.energy_left = 0; // later set to "native_mm_level.time"
484 game_mm.kettles_still_needed =
485 (native_mm_level.auto_count_kettles ? 0 : native_mm_level.kettles_needed);
486 game_mm.lights_still_needed = 0;
487 game_mm.num_keys = 0;
489 game_mm.level_solved = FALSE;
490 game_mm.game_over = FALSE;
491 game_mm.game_over_cause = 0;
493 game_mm.laser_overload_value = 0;
495 /* set global laser control values (must be set before "InitLaser()") */
496 laser.start_edge.x = 0;
497 laser.start_edge.y = 0;
498 laser.start_angle = 0;
500 for (i = 0; i < MAX_NUM_BEAMERS; i++)
501 laser.beamer[i][0].num = laser.beamer[i][1].num = 0;
503 laser.overloaded = FALSE;
504 laser.overload_value = 0;
505 laser.fuse_off = FALSE;
506 laser.fuse_x = laser.fuse_y = -1;
508 laser.dest_element = EL_EMPTY;
513 for (x = 0; x < lev_fieldx; x++)
515 for (y = 0; y < lev_fieldy; y++)
517 Feld[x][y] = Ur[x][y];
518 Hit[x][y] = Box[x][y] = 0;
520 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
521 Store[x][y] = Store2[x][y] = 0;
525 InitField(x, y, TRUE);
530 CloseDoor(DOOR_CLOSE_1);
536 void InitGameActions_MM()
538 int num_init_game_frames = INIT_GAME_ACTIONS_DELAY;
539 int cycle_steps_done = 0;
545 /* copy default game door content to main double buffer */
546 BlitBitmap(pix[PIX_DOOR], drawto,
547 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
551 DrawText(DX_LEVEL, DY_LEVEL,
552 int2str(level_nr, 2), FONT_TEXT_2);
553 DrawText(DX_KETTLES, DY_KETTLES,
554 int2str(game_mm.kettles_still_needed, 3), FONT_TEXT_2);
555 DrawText(DX_SCORE, DY_SCORE,
556 int2str(game_mm.score, 4), FONT_TEXT_2);
565 /* copy actual game door content to door double buffer for OpenDoor() */
566 BlitBitmap(drawto, pix[PIX_DB_DOOR],
567 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
571 OpenDoor(DOOR_OPEN_ALL);
574 for (i = 0; i <= num_init_game_frames; i++)
576 if (i == num_init_game_frames)
577 StopSound_MM(SND_MM_GAME_LEVELTIME_CHARGING);
578 else if (setup.sound_loops)
579 PlaySoundLoop_MM(SND_MM_GAME_LEVELTIME_CHARGING);
581 PlaySound_MM(SND_MM_GAME_LEVELTIME_CHARGING);
583 game_mm.energy_left = native_mm_level.time * i / num_init_game_frames;
585 UpdateAndDisplayGameControlValues();
587 while (cycle_steps_done < NUM_INIT_CYCLE_STEPS * i / num_init_game_frames)
589 InitCycleElements_RotateSingleStep();
599 if (setup.quick_doors)
605 if (setup.sound_music && num_bg_loops)
606 PlayMusic(level_nr % num_bg_loops);
612 void AddLaserEdge(int lx, int ly)
614 if (lx < -2 || ly < -2 || lx >= SXSIZE + 2 || ly >= SYSIZE + 2)
616 Error(ERR_WARN, "AddLaserEdge: out of bounds: %d, %d", lx, ly);
621 laser.edge[laser.num_edges].x = SX + 2 + lx;
622 laser.edge[laser.num_edges].y = SY + 2 + ly;
628 void AddDamagedField(int ex, int ey)
630 laser.damage[laser.num_damages].is_mirror = FALSE;
631 laser.damage[laser.num_damages].angle = laser.current_angle;
632 laser.damage[laser.num_damages].edge = laser.num_edges;
633 laser.damage[laser.num_damages].x = ex;
634 laser.damage[laser.num_damages].y = ey;
644 int last_x = laser.edge[laser.num_edges - 1].x - SX - 2;
645 int last_y = laser.edge[laser.num_edges - 1].y - SY - 2;
647 return ((x - last_x) * XS < 0 || (y - last_y) * YS < 0);
653 static int getMaskFromElement(int element)
655 if (IS_GRID(element))
656 return IMG_MM_MASK_GRID_1 + get_element_phase(element);
657 else if (IS_MCDUFFIN(element))
658 return IMG_MM_MASK_MCDUFFIN_RIGHT + get_element_phase(element);
659 else if (IS_RECTANGLE(element) || IS_DF_GRID(element))
660 return IMG_MM_MASK_RECTANGLE;
662 return IMG_MM_MASK_CIRCLE;
670 printf("ScanPixel: start scanning at (%d, %d) [%d, %d] [%d, %d]\n",
671 LX, LY, LX / TILEX, LY / TILEY, LX % TILEX, LY % TILEY);
674 /* follow laser beam until it hits something (at least the screen border) */
675 while (hit_mask == HIT_MASK_NO_HIT)
681 if (SX + LX < REAL_SX || SX + LX >= REAL_SX + FULL_SXSIZE ||
682 SY + LY < REAL_SY || SY + LY >= REAL_SY + FULL_SYSIZE)
684 printf("ScanPixel: touched screen border!\n");
690 for (i = 0; i < 4; i++)
692 int px = LX + (i % 2) * 2;
693 int py = LY + (i / 2) * 2;
696 int lx = (px + TILEX) / TILEX - 1; /* ...+TILEX...-1 to get correct */
697 int ly = (py + TILEY) / TILEY - 1; /* negative values! */
700 if (IN_LEV_FIELD(lx, ly))
702 int element = Feld[lx][ly];
704 if (element == EL_EMPTY || element == EL_EXPLODING_TRANSP)
708 else if (IS_WALL(element) || IS_WALL_CHANGING(element))
710 int pos = dy / MINI_TILEY * 2 + dx / MINI_TILEX;
712 pixel = ((element & (1 << pos)) ? 1 : 0);
716 int pos = getMaskFromElement(element) - IMG_MM_MASK_MCDUFFIN_RIGHT;
718 pixel = (mm_masks[pos][dy / 2][dx / 2] == 'X' ? 1 : 0);
723 pixel = (SX + px < REAL_SX || SX + px >= REAL_SX + FULL_SXSIZE ||
724 SY + py < REAL_SY || SY + py >= REAL_SY + FULL_SYSIZE);
727 if ((Sign[laser.current_angle] & (1 << i)) && pixel)
728 hit_mask |= (1 << i);
731 if (hit_mask == HIT_MASK_NO_HIT)
733 /* hit nothing -- go on with another step */
745 int end = 0, rf = laser.num_edges;
747 /* do not scan laser again after the game was lost for whatever reason */
748 if (game_mm.game_over)
751 laser.overloaded = FALSE;
752 laser.stops_inside_element = FALSE;
754 DrawLaser(0, DL_LASER_ENABLED);
757 printf("Start scanning with LX == %d, LY == %d, XS == %d, YS == %d\n",
765 if (laser.num_edges > MAX_LASER_LEN || laser.num_damages > MAX_LASER_LEN)
768 laser.overloaded = TRUE;
773 hit_mask = ScanPixel();
776 printf("Hit something at LX == %d, LY == %d, XS == %d, YS == %d\n",
780 /* hit something -- check out what it was */
781 ELX = (LX + XS) / TILEX;
782 ELY = (LY + YS) / TILEY;
785 printf("hit_mask (1) == '%x' (%d, %d) (%d, %d)\n",
786 hit_mask, LX, LY, ELX, ELY);
789 if (!IN_LEV_FIELD(ELX, ELY) || !IN_PIX_FIELD(LX, LY))
792 laser.dest_element = element;
797 if (hit_mask == (HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT))
799 /* we have hit the top-right and bottom-left element --
800 choose the bottom-left one */
801 /* !!! THIS CAN BE DONE MORE INTELLIGENTLY, FOR EXAMPLE, IF ONE
802 ELEMENT WAS STEEL AND THE OTHER ONE WAS ICE => ALWAYS CHOOSE
803 THE ICE AND MELT IT AWAY INSTEAD OF OVERLOADING LASER !!! */
804 ELX = (LX - 2) / TILEX;
805 ELY = (LY + 2) / TILEY;
808 if (hit_mask == (HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT))
810 /* we have hit the top-left and bottom-right element --
811 choose the top-left one */
812 /* !!! SEE ABOVE !!! */
813 ELX = (LX - 2) / TILEX;
814 ELY = (LY - 2) / TILEY;
818 printf("hit_mask (2) == '%x' (%d, %d) (%d, %d)\n",
819 hit_mask, LX, LY, ELX, ELY);
822 element = Feld[ELX][ELY];
823 laser.dest_element = element;
826 printf("Hit element %d at (%d, %d) [%d, %d] [%d, %d] [%d]\n",
829 LX % TILEX, LY % TILEY,
834 if (!IN_LEV_FIELD(ELX, ELY))
835 printf("WARNING! (1) %d, %d (%d)\n", ELX, ELY, element);
838 if (element == EL_EMPTY)
840 if (!HitOnlyAnEdge(element, hit_mask))
843 else if (element == EL_FUSE_ON)
845 if (HitPolarizer(element, hit_mask))
848 else if (IS_GRID(element) || IS_DF_GRID(element))
850 if (HitPolarizer(element, hit_mask))
853 else if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD ||
854 element == EL_GATE_STONE || element == EL_GATE_WOOD)
856 if (HitBlock(element, hit_mask))
863 else if (IS_MCDUFFIN(element))
865 if (HitLaserSource(element, hit_mask))
868 else if ((element >= EL_EXIT_CLOSED && element <= EL_EXIT_OPEN) ||
869 IS_RECEIVER(element))
871 if (HitLaserDestination(element, hit_mask))
874 else if (IS_WALL(element))
876 if (IS_WALL_STEEL(element) || IS_DF_WALL_STEEL(element))
878 if (HitReflectingWalls(element, hit_mask))
883 if (HitAbsorbingWalls(element, hit_mask))
889 if (HitElement(element, hit_mask))
894 DrawLaser(rf - 1, DL_LASER_ENABLED);
895 rf = laser.num_edges;
899 if (laser.dest_element != Feld[ELX][ELY])
901 printf("ALARM: laser.dest_element == %d, Feld[ELX][ELY] == %d\n",
902 laser.dest_element, Feld[ELX][ELY]);
906 if (!end && !laser.stops_inside_element && !StepBehind())
909 printf("ScanLaser: Go one step back\n");
915 AddLaserEdge(LX, LY);
919 DrawLaser(rf - 1, DL_LASER_ENABLED);
921 Ct = CT = FrameCounter;
924 if (!IN_LEV_FIELD(ELX, ELY))
925 printf("WARNING! (2) %d, %d\n", ELX, ELY);
929 void DrawLaserExt(int start_edge, int num_edges, int mode)
935 printf("DrawLaserExt: start_edge, num_edges, mode == %d, %d, %d\n",
936 start_edge, num_edges, mode);
941 Error(ERR_WARN, "DrawLaserExt: start_edge < 0");
948 Error(ERR_WARN, "DrawLaserExt: num_edges < 0");
954 if (mode == DL_LASER_DISABLED)
956 printf("DrawLaser: Delete laser from edge %d\n", start_edge);
960 /* now draw the laser to the backbuffer and (if enabled) to the screen */
961 DrawLines(drawto, &laser.edge[start_edge], num_edges,
962 (mode == DL_LASER_ENABLED ? pen_ray : pen_bg));
964 redraw_mask |= REDRAW_FIELD;
966 if (mode == DL_LASER_ENABLED)
969 /* after the laser was deleted, the "damaged" graphics must be restored */
970 if (laser.num_damages)
972 int damage_start = 0;
975 /* determine the starting edge, from which graphics need to be restored */
978 for (i = 0; i < laser.num_damages; i++)
980 if (laser.damage[i].edge == start_edge + 1)
989 /* restore graphics from this starting edge to the end of damage list */
990 for (i = damage_start; i < laser.num_damages; i++)
992 int lx = laser.damage[i].x;
993 int ly = laser.damage[i].y;
994 int element = Feld[lx][ly];
996 if (Hit[lx][ly] == laser.damage[i].edge)
997 if (!((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1000 if (Box[lx][ly] == laser.damage[i].edge)
1003 if (IS_DRAWABLE(element))
1004 DrawField_MM(lx, ly);
1007 elx = laser.damage[damage_start].x;
1008 ely = laser.damage[damage_start].y;
1009 element = Feld[elx][ely];
1012 if (IS_BEAMER(element))
1016 for (i = 0; i < laser.num_beamers; i++)
1017 printf("-> %d\n", laser.beamer_edge[i]);
1018 printf("DrawLaserExt: IS_BEAMER: [%d]: Hit[%d][%d] == %d [%d]\n",
1019 mode, elx, ely, Hit[elx][ely], start_edge);
1020 printf("DrawLaserExt: IS_BEAMER: %d / %d\n",
1021 get_element_angle(element), laser.damage[damage_start].angle);
1025 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1026 laser.num_beamers > 0 &&
1027 start_edge == laser.beamer_edge[laser.num_beamers - 1])
1029 /* element is outgoing beamer */
1030 laser.num_damages = damage_start + 1;
1032 if (IS_BEAMER(element))
1033 laser.current_angle = get_element_angle(element);
1037 /* element is incoming beamer or other element */
1038 laser.num_damages = damage_start;
1039 laser.current_angle = laser.damage[laser.num_damages].angle;
1044 /* no damages but McDuffin himself (who needs to be redrawn anyway) */
1046 elx = laser.start_edge.x;
1047 ely = laser.start_edge.y;
1048 element = Feld[elx][ely];
1051 laser.num_edges = start_edge + 1;
1052 if (start_edge == 0)
1053 laser.current_angle = laser.start_angle;
1055 LX = laser.edge[start_edge].x - (SX + 2);
1056 LY = laser.edge[start_edge].y - (SY + 2);
1057 XS = 2 * Step[laser.current_angle].x;
1058 YS = 2 * Step[laser.current_angle].y;
1061 printf("DrawLaser: Set (LX, LY) to (%d, %d) [%d]\n",
1067 if (IS_BEAMER(element) ||
1068 IS_FIBRE_OPTIC(element) ||
1069 IS_PACMAN(element) ||
1070 IS_POLAR(element) ||
1071 IS_POLAR_CROSS(element) ||
1072 element == EL_FUSE_ON)
1077 printf("element == %d\n", element);
1080 if (IS_22_5_ANGLE(laser.current_angle)) /* neither 90° nor 45° angle */
1081 step_size = ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) ? 4 : 3);
1085 if (IS_POLAR(element) || IS_POLAR_CROSS(element) ||
1086 ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1087 (laser.num_beamers == 0 ||
1088 start_edge != laser.beamer_edge[laser.num_beamers - 1])))
1090 /* element is incoming beamer or other element */
1091 step_size = -step_size;
1096 if (IS_BEAMER(element))
1098 printf("start_edge == %d, laser.beamer_edge == %d\n",
1099 start_edge, laser.beamer_edge);
1103 LX += step_size * XS;
1104 LY += step_size * YS;
1106 else if (element != EL_EMPTY)
1115 printf("DrawLaser: Finally: (LX, LY) to (%d, %d) [%d]\n",
1120 void DrawLaser(int start_edge, int mode)
1122 if (laser.num_edges - start_edge < 0)
1124 Error(ERR_WARN, "DrawLaser: laser.num_edges - start_edge < 0");
1129 /* check if laser is interrupted by beamer element */
1130 if (laser.num_beamers > 0 &&
1131 start_edge < laser.beamer_edge[laser.num_beamers - 1])
1133 if (mode == DL_LASER_ENABLED)
1136 int tmp_start_edge = start_edge;
1138 /* draw laser segments forward from the start to the last beamer */
1139 for (i = 0; i < laser.num_beamers; i++)
1141 int tmp_num_edges = laser.beamer_edge[i] - tmp_start_edge;
1143 if (tmp_num_edges <= 0)
1147 printf("DrawLaser: DL_LASER_ENABLED: i==%d: %d, %d\n",
1148 i, laser.beamer_edge[i], tmp_start_edge);
1151 DrawLaserExt(tmp_start_edge, tmp_num_edges, DL_LASER_ENABLED);
1153 tmp_start_edge = laser.beamer_edge[i];
1156 /* draw last segment from last beamer to the end */
1157 DrawLaserExt(tmp_start_edge, laser.num_edges - tmp_start_edge,
1163 int last_num_edges = laser.num_edges;
1164 int num_beamers = laser.num_beamers;
1166 /* delete laser segments backward from the end to the first beamer */
1167 for (i = num_beamers-1; i >= 0; i--)
1169 int tmp_num_edges = last_num_edges - laser.beamer_edge[i];
1171 if (laser.beamer_edge[i] - start_edge <= 0)
1174 DrawLaserExt(laser.beamer_edge[i], tmp_num_edges, DL_LASER_DISABLED);
1176 last_num_edges = laser.beamer_edge[i];
1177 laser.num_beamers--;
1181 if (last_num_edges - start_edge <= 0)
1182 printf("DrawLaser: DL_LASER_DISABLED: %d, %d\n",
1183 last_num_edges, start_edge);
1186 /* delete first segment from start to the first beamer */
1187 DrawLaserExt(start_edge, last_num_edges - start_edge, DL_LASER_DISABLED);
1192 DrawLaserExt(start_edge, laser.num_edges - start_edge, mode);
1196 boolean HitElement(int element, int hit_mask)
1198 if (HitOnlyAnEdge(element, hit_mask))
1201 if (IS_MOVING(ELX, ELY) || IS_BLOCKED(ELX, ELY))
1202 element = MovingOrBlocked2Element_MM(ELX, ELY);
1205 printf("HitElement (1): element == %d\n", element);
1209 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1210 printf("HitElement (%d): EXACT MATCH @ (%d, %d)\n", element, ELX, ELY);
1212 printf("HitElement (%d): FUZZY MATCH @ (%d, %d)\n", element, ELX, ELY);
1215 AddDamagedField(ELX, ELY);
1217 /* this is more precise: check if laser would go through the center */
1218 if ((ELX * TILEX + 14 - LX) * YS != (ELY * TILEY + 14 - LY) * XS)
1220 /* skip the whole element before continuing the scan */
1226 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1228 if (LX/TILEX > ELX || LY/TILEY > ELY)
1230 /* skipping scan positions to the right and down skips one scan
1231 position too much, because this is only the top left scan position
1232 of totally four scan positions (plus one to the right, one to the
1233 bottom and one to the bottom right) */
1243 printf("HitElement (2): element == %d\n", element);
1246 if (LX + 5 * XS < 0 ||
1256 printf("HitElement (3): element == %d\n", element);
1259 if (IS_POLAR(element) &&
1260 ((element - EL_POLAR_START) % 2 ||
1261 (element - EL_POLAR_START) / 2 != laser.current_angle % 8))
1263 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1265 laser.num_damages--;
1270 if (IS_POLAR_CROSS(element) &&
1271 (element - EL_POLAR_CROSS_START) != laser.current_angle % 4)
1273 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1275 laser.num_damages--;
1280 if (!IS_BEAMER(element) &&
1281 !IS_FIBRE_OPTIC(element) &&
1282 !IS_GRID_WOOD(element) &&
1283 element != EL_FUEL_EMPTY)
1286 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1287 printf("EXACT MATCH @ (%d, %d)\n", ELX, ELY);
1289 printf("FUZZY MATCH @ (%d, %d)\n", ELX, ELY);
1292 LX = ELX * TILEX + 14;
1293 LY = ELY * TILEY + 14;
1295 AddLaserEdge(LX, LY);
1298 if (IS_MIRROR(element) ||
1299 IS_MIRROR_FIXED(element) ||
1300 IS_POLAR(element) ||
1301 IS_POLAR_CROSS(element) ||
1302 IS_DF_MIRROR(element) ||
1303 IS_DF_MIRROR_AUTO(element) ||
1304 element == EL_PRISM ||
1305 element == EL_REFRACTOR)
1307 int current_angle = laser.current_angle;
1310 laser.num_damages--;
1312 AddDamagedField(ELX, ELY);
1314 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1317 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1319 if (IS_MIRROR(element) ||
1320 IS_MIRROR_FIXED(element) ||
1321 IS_DF_MIRROR(element) ||
1322 IS_DF_MIRROR_AUTO(element))
1323 laser.current_angle = get_mirrored_angle(laser.current_angle,
1324 get_element_angle(element));
1326 if (element == EL_PRISM || element == EL_REFRACTOR)
1327 laser.current_angle = RND(16);
1329 XS = 2 * Step[laser.current_angle].x;
1330 YS = 2 * Step[laser.current_angle].y;
1332 if (!IS_22_5_ANGLE(laser.current_angle)) /* 90° or 45° angle */
1337 LX += step_size * XS;
1338 LY += step_size * YS;
1341 /* draw sparkles on mirror */
1342 if ((IS_MIRROR(element) || IS_MIRROR_FIXED(element)) &&
1343 current_angle != laser.current_angle)
1345 MoveSprite(vp, &Pfeil[2], 4 + 16 * ELX, 5 + 16 * ELY + 1);
1349 if ((!IS_POLAR(element) && !IS_POLAR_CROSS(element)) &&
1350 current_angle != laser.current_angle)
1351 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1354 (get_opposite_angle(laser.current_angle) ==
1355 laser.damage[laser.num_damages - 1].angle ? TRUE : FALSE);
1357 return (laser.overloaded ? TRUE : FALSE);
1360 if (element == EL_FUEL_FULL)
1362 laser.stops_inside_element = TRUE;
1367 if (element == EL_BOMB || element == EL_MINE)
1369 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1371 if (element == EL_MINE)
1372 laser.overloaded = TRUE;
1375 if (element == EL_KETTLE ||
1376 element == EL_CELL ||
1377 element == EL_KEY ||
1378 element == EL_LIGHTBALL ||
1379 element == EL_PACMAN ||
1382 if (!IS_PACMAN(element))
1385 if (element == EL_PACMAN)
1388 if (element == EL_KETTLE || element == EL_CELL)
1390 if (game_mm.kettles_still_needed > 0)
1391 game_mm.kettles_still_needed--;
1395 if (game_mm.kettles_still_needed == 0)
1397 int exit_element = (element == EL_KETTLE ? EL_EXIT_OPEN : EL_RECEIVER);
1399 static int xy[4][2] =
1407 PlayLevelSound_MM(ELX, ELY, exit_element, MM_ACTION_OPENING);
1409 for (y = 0; y < lev_fieldy; y++)
1411 for (x = 0; x < lev_fieldx; x++)
1413 /* initiate opening animation of exit door */
1414 if (Feld[x][y] == EL_EXIT_CLOSED)
1415 Feld[x][y] = EL_EXIT_OPENING;
1417 /* remove field that blocks receiver */
1418 if (IS_RECEIVER(Feld[x][y]))
1420 int phase = Feld[x][y] - EL_RECEIVER_START;
1421 int blocking_x, blocking_y;
1423 blocking_x = x + xy[phase][0];
1424 blocking_y = y + xy[phase][1];
1426 if (IN_LEV_FIELD(blocking_x, blocking_y))
1428 Feld[blocking_x][blocking_y] = EL_EMPTY;
1430 DrawField_MM(blocking_x, blocking_y);
1436 DrawLaser(0, DL_LASER_ENABLED);
1439 else if (element == EL_KEY)
1443 else if (element == EL_LIGHTBALL)
1447 else if (IS_PACMAN(element))
1449 DeletePacMan(ELX, ELY);
1456 if (element == EL_LIGHTBULB_OFF || element == EL_LIGHTBULB_ON)
1458 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1460 DrawLaser(0, DL_LASER_ENABLED);
1462 if (Feld[ELX][ELY] == EL_LIGHTBULB_OFF)
1464 Feld[ELX][ELY] = EL_LIGHTBULB_ON;
1465 game_mm.lights_still_needed--;
1469 Feld[ELX][ELY] = EL_LIGHTBULB_OFF;
1470 game_mm.lights_still_needed++;
1473 DrawField_MM(ELX, ELY);
1474 DrawLaser(0, DL_LASER_ENABLED);
1479 laser.stops_inside_element = TRUE;
1485 printf("HitElement (4): element == %d\n", element);
1488 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1489 laser.num_beamers < MAX_NUM_BEAMERS &&
1490 laser.beamer[BEAMER_NR(element)][1].num)
1492 int beamer_angle = get_element_angle(element);
1493 int beamer_nr = BEAMER_NR(element);
1497 printf("HitElement (BEAMER): element == %d\n", element);
1500 laser.num_damages--;
1502 if (IS_FIBRE_OPTIC(element) ||
1503 laser.current_angle == get_opposite_angle(beamer_angle))
1507 LX = ELX * TILEX + 14;
1508 LY = ELY * TILEY + 14;
1510 AddLaserEdge(LX, LY);
1511 AddDamagedField(ELX, ELY);
1513 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1516 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1518 pos = (ELX == laser.beamer[beamer_nr][0].x &&
1519 ELY == laser.beamer[beamer_nr][0].y ? 1 : 0);
1520 ELX = laser.beamer[beamer_nr][pos].x;
1521 ELY = laser.beamer[beamer_nr][pos].y;
1522 LX = ELX * TILEX + 14;
1523 LY = ELY * TILEY + 14;
1525 if (IS_BEAMER(element))
1527 laser.current_angle = get_element_angle(Feld[ELX][ELY]);
1528 XS = 2 * Step[laser.current_angle].x;
1529 YS = 2 * Step[laser.current_angle].y;
1532 laser.beamer_edge[laser.num_beamers] = laser.num_edges;
1534 AddLaserEdge(LX, LY);
1535 AddDamagedField(ELX, ELY);
1537 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1540 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1542 if (laser.current_angle == (laser.current_angle >> 1) << 1)
1547 LX += step_size * XS;
1548 LY += step_size * YS;
1550 laser.num_beamers++;
1559 boolean HitOnlyAnEdge(int element, int hit_mask)
1561 /* check if the laser hit only the edge of an element and, if so, go on */
1564 printf("LX, LY, hit_mask == %d, %d, %d\n", LX, LY, hit_mask);
1567 if ((hit_mask == HIT_MASK_TOPLEFT ||
1568 hit_mask == HIT_MASK_TOPRIGHT ||
1569 hit_mask == HIT_MASK_BOTTOMLEFT ||
1570 hit_mask == HIT_MASK_BOTTOMRIGHT) &&
1571 laser.current_angle % 4) /* angle is not 90° */
1575 if (hit_mask == HIT_MASK_TOPLEFT)
1580 else if (hit_mask == HIT_MASK_TOPRIGHT)
1585 else if (hit_mask == HIT_MASK_BOTTOMLEFT)
1590 else /* (hit_mask == HIT_MASK_BOTTOMRIGHT) */
1596 AddDamagedField((LX + 2 * dx) / TILEX, (LY + 2 * dy) / TILEY);
1602 printf("[HitOnlyAnEdge() == TRUE]\n");
1609 printf("[HitOnlyAnEdge() == FALSE]\n");
1615 boolean HitPolarizer(int element, int hit_mask)
1617 if (HitOnlyAnEdge(element, hit_mask))
1620 if (IS_DF_GRID(element))
1622 int grid_angle = get_element_angle(element);
1625 printf("HitPolarizer: angle: grid == %d, laser == %d\n",
1626 grid_angle, laser.current_angle);
1629 AddLaserEdge(LX, LY);
1630 AddDamagedField(ELX, ELY);
1633 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1635 if (laser.current_angle == grid_angle ||
1636 laser.current_angle == get_opposite_angle(grid_angle))
1638 /* skip the whole element before continuing the scan */
1644 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1646 if (LX/TILEX > ELX || LY/TILEY > ELY)
1648 /* skipping scan positions to the right and down skips one scan
1649 position too much, because this is only the top left scan position
1650 of totally four scan positions (plus one to the right, one to the
1651 bottom and one to the bottom right) */
1657 AddLaserEdge(LX, LY);
1663 printf("HitPolarizer: LX, LY == %d, %d [%d, %d] [%d, %d]\n",
1665 LX / TILEX, LY / TILEY,
1666 LX % TILEX, LY % TILEY);
1671 else if (IS_GRID_STEEL_FIXED(element) || IS_GRID_STEEL_AUTO(element))
1673 return HitReflectingWalls(element, hit_mask);
1677 return HitAbsorbingWalls(element, hit_mask);
1680 else if (IS_GRID_STEEL(element))
1682 return HitReflectingWalls(element, hit_mask);
1684 else /* IS_GRID_WOOD */
1686 return HitAbsorbingWalls(element, hit_mask);
1692 boolean HitBlock(int element, int hit_mask)
1694 boolean check = FALSE;
1696 if ((element == EL_GATE_STONE || element == EL_GATE_WOOD) &&
1697 game_mm.num_keys == 0)
1700 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1703 int ex = ELX * TILEX + 14;
1704 int ey = ELY * TILEY + 14;
1708 for (i = 1; i < 32; i++)
1713 if ((x == ex || x == ex + 1) && (y == ey || y == ey + 1))
1718 if (check && (element == EL_BLOCK_WOOD || element == EL_GATE_WOOD))
1719 return HitAbsorbingWalls(element, hit_mask);
1723 AddLaserEdge(LX - XS, LY - YS);
1724 AddDamagedField(ELX, ELY);
1727 Box[ELX][ELY] = laser.num_edges;
1729 return HitReflectingWalls(element, hit_mask);
1732 if (element == EL_GATE_STONE || element == EL_GATE_WOOD)
1734 int xs = XS / 2, ys = YS / 2;
1735 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1736 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1738 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1739 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1741 laser.overloaded = (element == EL_GATE_STONE);
1746 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1747 (hit_mask == HIT_MASK_TOP ||
1748 hit_mask == HIT_MASK_LEFT ||
1749 hit_mask == HIT_MASK_RIGHT ||
1750 hit_mask == HIT_MASK_BOTTOM))
1751 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1752 hit_mask == HIT_MASK_BOTTOM),
1753 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1754 hit_mask == HIT_MASK_RIGHT));
1755 AddLaserEdge(LX, LY);
1761 if (element == EL_GATE_STONE && Box[ELX][ELY])
1763 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
1775 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1777 int xs = XS / 2, ys = YS / 2;
1778 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1779 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1781 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1782 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1784 laser.overloaded = (element == EL_BLOCK_STONE);
1789 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1790 (hit_mask == HIT_MASK_TOP ||
1791 hit_mask == HIT_MASK_LEFT ||
1792 hit_mask == HIT_MASK_RIGHT ||
1793 hit_mask == HIT_MASK_BOTTOM))
1794 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1795 hit_mask == HIT_MASK_BOTTOM),
1796 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1797 hit_mask == HIT_MASK_RIGHT));
1798 AddDamagedField(ELX, ELY);
1800 LX = ELX * TILEX + 14;
1801 LY = ELY * TILEY + 14;
1803 AddLaserEdge(LX, LY);
1805 laser.stops_inside_element = TRUE;
1813 boolean HitLaserSource(int element, int hit_mask)
1815 if (HitOnlyAnEdge(element, hit_mask))
1818 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1820 laser.overloaded = TRUE;
1825 boolean HitLaserDestination(int element, int hit_mask)
1827 if (HitOnlyAnEdge(element, hit_mask))
1830 if (element != EL_EXIT_OPEN &&
1831 !(IS_RECEIVER(element) &&
1832 game_mm.kettles_still_needed == 0 &&
1833 laser.current_angle == get_opposite_angle(get_element_angle(element))))
1835 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1840 if (IS_RECEIVER(element) ||
1841 (IS_22_5_ANGLE(laser.current_angle) &&
1842 (ELX != (LX + 6 * XS) / TILEX ||
1843 ELY != (LY + 6 * YS) / TILEY ||
1852 LX = ELX * TILEX + 14;
1853 LY = ELY * TILEY + 14;
1855 laser.stops_inside_element = TRUE;
1858 AddLaserEdge(LX, LY);
1859 AddDamagedField(ELX, ELY);
1861 if (game_mm.lights_still_needed == 0)
1862 game_mm.level_solved = TRUE;
1867 boolean HitReflectingWalls(int element, int hit_mask)
1869 /* check if laser hits side of a wall with an angle that is not 90° */
1870 if (!IS_90_ANGLE(laser.current_angle) && (hit_mask == HIT_MASK_TOP ||
1871 hit_mask == HIT_MASK_LEFT ||
1872 hit_mask == HIT_MASK_RIGHT ||
1873 hit_mask == HIT_MASK_BOTTOM))
1875 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1880 if (!IS_DF_GRID(element))
1881 AddLaserEdge(LX, LY);
1883 /* check if laser hits wall with an angle of 45° */
1884 if (!IS_22_5_ANGLE(laser.current_angle))
1886 if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1889 laser.current_angle = get_mirrored_angle(laser.current_angle,
1892 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1895 laser.current_angle = get_mirrored_angle(laser.current_angle,
1899 AddLaserEdge(LX, LY);
1901 XS = 2 * Step[laser.current_angle].x;
1902 YS = 2 * Step[laser.current_angle].y;
1906 else if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1908 laser.current_angle = get_mirrored_angle(laser.current_angle,
1913 if (!IS_DF_GRID(element))
1914 AddLaserEdge(LX, LY);
1919 if (!IS_DF_GRID(element))
1920 AddLaserEdge(LX, LY + YS / 2);
1923 if (!IS_DF_GRID(element))
1924 AddLaserEdge(LX, LY);
1927 YS = 2 * Step[laser.current_angle].y;
1931 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1933 laser.current_angle = get_mirrored_angle(laser.current_angle,
1938 if (!IS_DF_GRID(element))
1939 AddLaserEdge(LX, LY);
1944 if (!IS_DF_GRID(element))
1945 AddLaserEdge(LX + XS / 2, LY);
1948 if (!IS_DF_GRID(element))
1949 AddLaserEdge(LX, LY);
1952 XS = 2 * Step[laser.current_angle].x;
1958 /* reflection at the edge of reflecting DF style wall */
1959 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1961 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1962 hit_mask == HIT_MASK_TOPRIGHT) ||
1963 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1964 hit_mask == HIT_MASK_TOPLEFT) ||
1965 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1966 hit_mask == HIT_MASK_BOTTOMLEFT) ||
1967 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1968 hit_mask == HIT_MASK_BOTTOMRIGHT))
1971 (hit_mask == HIT_MASK_TOPRIGHT || hit_mask == HIT_MASK_BOTTOMLEFT ?
1972 ANG_MIRROR_135 : ANG_MIRROR_45);
1974 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
1976 AddDamagedField(ELX, ELY);
1977 AddLaserEdge(LX, LY);
1979 laser.current_angle = get_mirrored_angle(laser.current_angle,
1987 AddLaserEdge(LX, LY);
1993 /* reflection inside an edge of reflecting DF style wall */
1994 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1996 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1997 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT)) ||
1998 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1999 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMRIGHT)) ||
2000 ((laser.current_angle == 9 || laser.current_angle == 11) &&
2001 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT)) ||
2002 ((laser.current_angle == 13 || laser.current_angle == 15) &&
2003 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPLEFT)))
2006 (hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT) ||
2007 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT) ?
2008 ANG_MIRROR_135 : ANG_MIRROR_45);
2010 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
2013 AddDamagedField(ELX, ELY);
2016 AddLaserEdge(LX - XS, LY - YS);
2017 AddLaserEdge(LX - XS + (ABS(XS) == 4 ? XS/2 : 0),
2018 LY - YS + (ABS(YS) == 4 ? YS/2 : 0));
2020 laser.current_angle = get_mirrored_angle(laser.current_angle,
2028 AddLaserEdge(LX, LY);
2034 /* check if laser hits DF style wall with an angle of 90° */
2035 if (IS_DF_WALL(element) && IS_90_ANGLE(laser.current_angle))
2037 if ((IS_HORIZ_ANGLE(laser.current_angle) &&
2038 (!(hit_mask & HIT_MASK_TOP) || !(hit_mask & HIT_MASK_BOTTOM))) ||
2039 (IS_VERT_ANGLE(laser.current_angle) &&
2040 (!(hit_mask & HIT_MASK_LEFT) || !(hit_mask & HIT_MASK_RIGHT))))
2042 static int last_LX = 0, last_LY = 0, last_hit_mask = 0;
2044 /* laser at last step touched nothing or the same side of the wall */
2045 if (LX != last_LX || LY != last_LY || hit_mask == last_hit_mask)
2047 AddDamagedField(ELX, ELY);
2054 last_hit_mask = hit_mask;
2061 if (!HitOnlyAnEdge(element, hit_mask))
2063 laser.overloaded = TRUE;
2071 boolean HitAbsorbingWalls(int element, int hit_mask)
2073 if (HitOnlyAnEdge(element, hit_mask))
2077 (hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT))
2079 AddLaserEdge(LX - XS, LY - YS);
2086 (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM))
2088 AddLaserEdge(LX - XS, LY - YS);
2094 if (IS_WALL_WOOD(element) ||
2095 IS_DF_WALL_WOOD(element) ||
2096 IS_GRID_WOOD(element) ||
2097 IS_GRID_WOOD_FIXED(element) ||
2098 IS_GRID_WOOD_AUTO(element) ||
2099 element == EL_FUSE_ON ||
2100 element == EL_BLOCK_WOOD ||
2101 element == EL_GATE_WOOD)
2103 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_HITTING);
2108 if (IS_WALL_ICE(element))
2112 mask = (LX + XS) / MINI_TILEX - ELX * 2 + 1; /* Quadrant (horizontal) */
2113 mask <<= (((LY + YS) / MINI_TILEY - ELY * 2) > 0) * 2; /* || (vertical) */
2115 /* check if laser hits wall with an angle of 90° */
2116 if (IS_90_ANGLE(laser.current_angle))
2117 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
2119 if (mask == 1 || mask == 2 || mask == 4 || mask == 8)
2123 for (i = 0; i < 4; i++)
2125 if (mask == (1 << i) && (XS > 0) == (i % 2) && (YS > 0) == (i / 2))
2126 mask = 15 - (8 >> i);
2127 else if (ABS(XS) == 4 &&
2129 (XS > 0) == (i % 2) &&
2130 (YS < 0) == (i / 2))
2131 mask = 3 + (i / 2) * 9;
2132 else if (ABS(YS) == 4 &&
2134 (XS < 0) == (i % 2) &&
2135 (YS > 0) == (i / 2))
2136 mask = 5 + (i % 2) * 5;
2140 laser.wall_mask = mask;
2142 else if (IS_WALL_AMOEBA(element))
2144 int elx = (LX - 2 * XS) / TILEX;
2145 int ely = (LY - 2 * YS) / TILEY;
2146 int element2 = Feld[elx][ely];
2149 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element2))
2151 laser.dest_element = EL_EMPTY;
2159 mask = (LX - 2 * XS) / 16 - ELX * 2 + 1;
2160 mask <<= ((LY - 2 * YS) / 16 - ELY * 2) * 2;
2162 if (IS_90_ANGLE(laser.current_angle))
2163 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
2165 laser.dest_element = element2 | EL_WALL_AMOEBA;
2167 laser.wall_mask = mask;
2173 void OpenExit(int x, int y)
2177 if (!MovDelay[x][y]) /* next animation frame */
2178 MovDelay[x][y] = 4 * delay;
2180 if (MovDelay[x][y]) /* wait some time before next frame */
2185 phase = MovDelay[x][y] / delay;
2187 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2188 DrawGraphicAnimation_MM(x, y, IMG_MM_EXIT_OPENING, 3 - phase);
2190 if (!MovDelay[x][y])
2192 Feld[x][y] = EL_EXIT_OPEN;
2198 void OpenSurpriseBall(int x, int y)
2202 if (!MovDelay[x][y]) /* next animation frame */
2203 MovDelay[x][y] = 50 * delay;
2205 if (MovDelay[x][y]) /* wait some time before next frame */
2209 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2212 int graphic = el2gfx(Store[x][y]);
2214 int dx = RND(26), dy = RND(26);
2216 getGraphicSource(graphic, 0, &bitmap, &gx, &gy);
2218 BlitBitmap(bitmap, drawto, gx + dx, gy + dy, 6, 6,
2219 SX + x * TILEX + dx, SY + y * TILEY + dy);
2221 MarkTileDirty(x, y);
2224 if (!MovDelay[x][y])
2226 Feld[x][y] = Store[x][y];
2235 void MeltIce(int x, int y)
2240 if (!MovDelay[x][y]) /* next animation frame */
2241 MovDelay[x][y] = frames * delay;
2243 if (MovDelay[x][y]) /* wait some time before next frame */
2246 int wall_mask = Store2[x][y];
2247 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_ICE;
2250 phase = frames - MovDelay[x][y] / delay - 1;
2252 if (!MovDelay[x][y])
2256 Feld[x][y] = real_element & (wall_mask ^ 0xFF);
2257 Store[x][y] = Store2[x][y] = 0;
2259 DrawWalls_MM(x, y, Feld[x][y]);
2261 if (Feld[x][y] == EL_WALL_ICE)
2262 Feld[x][y] = EL_EMPTY;
2264 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
2265 if (laser.damage[i].is_mirror)
2269 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
2271 DrawLaser(0, DL_LASER_DISABLED);
2275 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2277 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2279 laser.redraw = TRUE;
2284 void GrowAmoeba(int x, int y)
2289 if (!MovDelay[x][y]) /* next animation frame */
2290 MovDelay[x][y] = frames * delay;
2292 if (MovDelay[x][y]) /* wait some time before next frame */
2295 int wall_mask = Store2[x][y];
2296 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_AMOEBA;
2299 phase = MovDelay[x][y] / delay;
2301 if (!MovDelay[x][y])
2303 Feld[x][y] = real_element;
2304 Store[x][y] = Store2[x][y] = 0;
2306 DrawWalls_MM(x, y, Feld[x][y]);
2307 DrawLaser(0, DL_LASER_ENABLED);
2309 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2311 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2316 static void Explode_MM(int x, int y, int phase, int mode)
2318 int num_phase = 9, delay = 2;
2319 int last_phase = num_phase * delay;
2320 int half_phase = (num_phase / 2) * delay;
2322 laser.redraw = TRUE;
2324 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2326 int center_element = Feld[x][y];
2328 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2330 /* put moving element to center field (and let it explode there) */
2331 center_element = MovingOrBlocked2Element_MM(x, y);
2332 RemoveMovingField_MM(x, y);
2334 Feld[x][y] = center_element;
2337 if (center_element == EL_BOMB || IS_MCDUFFIN(center_element))
2338 Store[x][y] = center_element;
2340 Store[x][y] = EL_EMPTY;
2342 Store2[x][y] = mode;
2343 Feld[x][y] = EL_EXPLODING_OPAQUE;
2344 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2350 Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
2352 if (phase == half_phase)
2354 Feld[x][y] = EL_EXPLODING_TRANSP;
2356 if (x == ELX && y == ELY)
2360 if (phase == last_phase)
2362 if (Store[x][y] == EL_BOMB)
2364 DrawLaser(0, DL_LASER_DISABLED);
2367 Bang_MM(laser.start_edge.x, laser.start_edge.y);
2368 Store[x][y] = EL_EMPTY;
2370 game_mm.game_over = TRUE;
2371 game_mm.game_over_cause = GAME_OVER_BOMB;
2373 laser.overloaded = FALSE;
2375 else if (IS_MCDUFFIN(Store[x][y]))
2377 Store[x][y] = EL_EMPTY;
2380 Feld[x][y] = Store[x][y];
2381 Store[x][y] = Store2[x][y] = 0;
2382 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2384 InitField(x, y, FALSE);
2387 else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2389 int graphic = IMG_MM_DEFAULT_EXPLODING;
2390 int graphic_phase = (phase / delay - 1);
2394 if (Store2[x][y] == EX_KETTLE)
2396 if (graphic_phase < 3)
2398 graphic = IMG_MM_KETTLE_EXPLODING;
2400 else if (graphic_phase < 5)
2406 graphic = IMG_EMPTY;
2410 else if (Store2[x][y] == EX_SHORT)
2412 if (graphic_phase < 4)
2418 graphic = IMG_EMPTY;
2423 getGraphicSource(graphic, graphic_phase, &bitmap, &src_x, &src_y);
2425 BlitBitmap(bitmap, drawto_field, src_x, src_y, TILEX, TILEY,
2426 FX + x * TILEX, FY + y * TILEY);
2428 MarkTileDirty(x, y);
2432 static void Bang_MM(int x, int y)
2434 int element = Feld[x][y];
2435 int mode = EX_NORMAL;
2438 DrawLaser(0, DL_LASER_ENABLED);
2457 if (IS_PACMAN(element))
2458 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2459 else if (element == EL_BOMB || IS_MCDUFFIN(element))
2460 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2461 else if (element == EL_KEY)
2462 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2464 PlayLevelSound_MM(x, y, element, MM_ACTION_EXPLODING);
2466 Explode_MM(x, y, EX_PHASE_START, mode);
2469 void TurnRound(int x, int y)
2481 { 0, 0 }, { 0, 0 }, { 0, 0 },
2486 int left, right, back;
2490 { MV_DOWN, MV_UP, MV_RIGHT },
2491 { MV_UP, MV_DOWN, MV_LEFT },
2493 { MV_LEFT, MV_RIGHT, MV_DOWN },
2494 { 0,0,0 }, { 0,0,0 }, { 0,0,0 },
2495 { MV_RIGHT, MV_LEFT, MV_UP }
2498 int element = Feld[x][y];
2499 int old_move_dir = MovDir[x][y];
2500 int right_dir = turn[old_move_dir].right;
2501 int back_dir = turn[old_move_dir].back;
2502 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
2503 int right_x = x + right_dx, right_y = y + right_dy;
2505 if (element == EL_PACMAN)
2507 boolean can_turn_right = FALSE;
2509 if (IN_LEV_FIELD(right_x, right_y) &&
2510 IS_EATABLE4PACMAN(Feld[right_x][right_y]))
2511 can_turn_right = TRUE;
2514 MovDir[x][y] = right_dir;
2516 MovDir[x][y] = back_dir;
2522 static void StartMoving_MM(int x, int y)
2524 int element = Feld[x][y];
2529 if (CAN_MOVE(element))
2533 if (MovDelay[x][y]) /* wait some time before next movement */
2541 /* now make next step */
2543 Moving2Blocked_MM(x, y, &newx, &newy); /* get next screen position */
2545 if (element == EL_PACMAN &&
2546 IN_LEV_FIELD(newx, newy) && IS_EATABLE4PACMAN(Feld[newx][newy]) &&
2547 !ObjHit(newx, newy, HIT_POS_CENTER))
2549 Store[newx][newy] = Feld[newx][newy];
2550 Feld[newx][newy] = EL_EMPTY;
2552 DrawField_MM(newx, newy);
2554 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy) ||
2555 ObjHit(newx, newy, HIT_POS_CENTER))
2557 /* object was running against a wall */
2564 InitMovingField_MM(x, y, MovDir[x][y]);
2568 ContinueMoving_MM(x, y);
2571 static void ContinueMoving_MM(int x, int y)
2573 int element = Feld[x][y];
2574 int direction = MovDir[x][y];
2575 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2576 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2577 int horiz_move = (dx!=0);
2578 int newx = x + dx, newy = y + dy;
2579 int step = (horiz_move ? dx : dy) * TILEX / 8;
2581 MovPos[x][y] += step;
2583 if (ABS(MovPos[x][y]) >= TILEX) /* object reached its destination */
2585 Feld[x][y] = EL_EMPTY;
2586 Feld[newx][newy] = element;
2588 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2589 MovDelay[newx][newy] = 0;
2591 if (!CAN_MOVE(element))
2592 MovDir[newx][newy] = 0;
2595 DrawField_MM(newx, newy);
2597 Stop[newx][newy] = TRUE;
2599 if (element == EL_PACMAN)
2601 if (Store[newx][newy] == EL_BOMB)
2602 Bang_MM(newx, newy);
2604 if (IS_WALL_AMOEBA(Store[newx][newy]) &&
2605 (LX + 2 * XS) / TILEX == newx &&
2606 (LY + 2 * YS) / TILEY == newy)
2613 else /* still moving on */
2618 laser.redraw = TRUE;
2621 void ClickElement(int x, int y, int button)
2623 static unsigned int click_delay = 0;
2624 static int click_delay_value = CLICK_DELAY;
2625 static boolean new_button = TRUE;
2628 /* do not rotate objects hit by the laser after the game was solved */
2629 if (game_mm.level_solved && Hit[x][y])
2632 if (button == MB_RELEASED)
2635 click_delay_value = CLICK_DELAY;
2637 /* release eventually hold auto-rotating mirror */
2638 RotateMirror(x, y, MB_RELEASED);
2643 if (!FrameReached(&click_delay, click_delay_value) && !new_button)
2646 if (button == MB_MIDDLEBUTTON) /* middle button has no function */
2649 if (!IN_LEV_FIELD(x, y))
2652 if (Feld[x][y] == EL_EMPTY)
2655 element = Feld[x][y];
2657 if (IS_MIRROR(element) ||
2658 IS_BEAMER(element) ||
2659 IS_POLAR(element) ||
2660 IS_POLAR_CROSS(element) ||
2661 IS_DF_MIRROR(element) ||
2662 IS_DF_MIRROR_AUTO(element))
2664 RotateMirror(x, y, button);
2666 else if (IS_MCDUFFIN(element))
2668 if (!laser.fuse_off)
2670 DrawLaser(0, DL_LASER_DISABLED);
2677 element = get_rotated_element(element, BUTTON_ROTATION(button));
2678 laser.start_angle = get_element_angle(element);
2682 Feld[x][y] = element;
2689 if (!laser.fuse_off)
2692 else if (element == EL_FUSE_ON && laser.fuse_off)
2694 if (x != laser.fuse_x || y != laser.fuse_y)
2697 laser.fuse_off = FALSE;
2698 laser.fuse_x = laser.fuse_y = -1;
2700 DrawGraphic_MM(x, y, IMG_MM_FUSE_ACTIVE);
2703 else if (element == EL_FUSE_ON && !laser.fuse_off && new_button)
2705 laser.fuse_off = TRUE;
2708 laser.overloaded = FALSE;
2710 DrawLaser(0, DL_LASER_DISABLED);
2711 DrawGraphic_MM(x, y, IMG_MM_FUSE);
2713 else if (element == EL_LIGHTBALL)
2717 DrawLaser(0, DL_LASER_ENABLED);
2720 click_delay_value = (new_button ? CLICK_DELAY_FIRST : CLICK_DELAY);
2724 void RotateMirror(int x, int y, int button)
2726 static int hold_x = -1, hold_y = -1;
2728 if (button == MB_RELEASED)
2730 /* release eventually hold auto-rotating mirror */
2737 if (IS_MIRROR(Feld[x][y]) ||
2738 IS_POLAR_CROSS(Feld[x][y]) ||
2739 IS_POLAR(Feld[x][y]) ||
2740 IS_BEAMER(Feld[x][y]) ||
2741 IS_DF_MIRROR(Feld[x][y]) ||
2742 IS_GRID_STEEL_AUTO(Feld[x][y]) ||
2743 IS_GRID_WOOD_AUTO(Feld[x][y]))
2745 Feld[x][y] = get_rotated_element(Feld[x][y], BUTTON_ROTATION(button));
2747 else if (IS_DF_MIRROR_AUTO(Feld[x][y]))
2749 if (button == MB_LEFTBUTTON)
2751 /* left mouse button only for manual adjustment, no auto-rotating;
2752 freeze mirror for until mouse button released */
2756 else if (button == MB_RIGHTBUTTON && (hold_x != x || hold_y != y))
2758 Feld[x][y] = get_rotated_element(Feld[x][y], ROTATE_RIGHT);
2762 if (IS_GRID_STEEL_AUTO(Feld[x][y]) || IS_GRID_WOOD_AUTO(Feld[x][y]))
2764 int edge = Hit[x][y];
2770 DrawLaser(edge - 1, DL_LASER_DISABLED);
2774 else if (ObjHit(x, y, HIT_POS_CENTER))
2776 int edge = Hit[x][y];
2780 Error(ERR_WARN, "RotateMirror: inconsistent field Hit[][]!\n");
2784 DrawLaser(edge - 1, DL_LASER_DISABLED);
2791 if (ObjHit(x, y, HIT_POS_EDGE | HIT_POS_BETWEEN))
2796 if ((IS_BEAMER(Feld[x][y]) ||
2797 IS_POLAR(Feld[x][y]) ||
2798 IS_POLAR_CROSS(Feld[x][y])) && x == ELX && y == ELY)
2802 if (IS_BEAMER(Feld[x][y]))
2805 printf("TEST (%d, %d) [%d] [%d]\n",
2807 laser.beamer_edge, laser.beamer[1].num);
2817 DrawLaser(0, DL_LASER_ENABLED);
2821 void AutoRotateMirrors()
2823 static unsigned int rotate_delay = 0;
2826 if (!FrameReached(&rotate_delay, AUTO_ROTATE_DELAY))
2829 for (x = 0; x < lev_fieldx; x++)
2831 for (y = 0; y < lev_fieldy; y++)
2833 int element = Feld[x][y];
2835 /* do not rotate objects hit by the laser after the game was solved */
2836 if (game_mm.level_solved && Hit[x][y])
2839 if (IS_DF_MIRROR_AUTO(element) ||
2840 IS_GRID_WOOD_AUTO(element) ||
2841 IS_GRID_STEEL_AUTO(element) ||
2842 element == EL_REFRACTOR)
2843 RotateMirror(x, y, MB_RIGHTBUTTON);
2848 boolean ObjHit(int obx, int oby, int bits)
2855 if (bits & HIT_POS_CENTER)
2857 if (ReadPixel(drawto, SX + obx + 15, SY + oby + 15) == pen_ray)
2861 if (bits & HIT_POS_EDGE)
2863 for (i = 0; i < 4; i++)
2864 if (ReadPixel(drawto,
2865 SX + obx + 31 * (i % 2),
2866 SY + oby + 31 * (i / 2)) == pen_ray)
2870 if (bits & HIT_POS_BETWEEN)
2872 for (i = 0; i < 4; i++)
2873 if (ReadPixel(drawto,
2874 SX + 4 + obx + 22 * (i % 2),
2875 SY + 4 + oby + 22 * (i / 2)) == pen_ray)
2882 void DeletePacMan(int px, int py)
2888 if (game_mm.num_pacman <= 1)
2890 game_mm.num_pacman = 0;
2894 for (i = 0; i < game_mm.num_pacman; i++)
2895 if (game_mm.pacman[i].x == px && game_mm.pacman[i].y == py)
2898 game_mm.num_pacman--;
2900 for (j = i; j < game_mm.num_pacman; j++)
2902 game_mm.pacman[j].x = game_mm.pacman[j + 1].x;
2903 game_mm.pacman[j].y = game_mm.pacman[j + 1].y;
2904 game_mm.pacman[j].dir = game_mm.pacman[j + 1].dir;
2908 void ColorCycling(void)
2910 static int CC, Cc = 0;
2912 static int color, old = 0xF00, new = 0x010, mult = 1;
2913 static unsigned short red, green, blue;
2915 if (color_status == STATIC_COLORS)
2920 if (CC < Cc || CC > Cc + 2)
2924 color = old + new * mult;
2930 if (ABS(mult) == 16)
2940 red = 0x0e00 * ((color & 0xF00) >> 8);
2941 green = 0x0e00 * ((color & 0x0F0) >> 4);
2942 blue = 0x0e00 * ((color & 0x00F));
2943 SetRGB(pen_magicolor[0], red, green, blue);
2945 red = 0x1111 * ((color & 0xF00) >> 8);
2946 green = 0x1111 * ((color & 0x0F0) >> 4);
2947 blue = 0x1111 * ((color & 0x00F));
2948 SetRGB(pen_magicolor[1], red, green, blue);
2952 static void GameActions_MM_Ext(struct MouseActionInfo action, boolean warp_mode)
2954 static unsigned int pacman_delay = 0;
2955 static unsigned int energy_delay = 0;
2956 static unsigned int overload_delay = 0;
2962 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2965 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2967 element = Feld[x][y];
2969 if (!IS_MOVING(x, y) && CAN_MOVE(element))
2970 StartMoving_MM(x, y);
2971 else if (IS_MOVING(x, y))
2972 ContinueMoving_MM(x, y);
2973 else if (IS_EXPLODING(element))
2974 Explode_MM(x, y, Frame[x][y], EX_NORMAL);
2975 else if (element == EL_EXIT_OPENING)
2977 else if (element == EL_GRAY_BALL_OPENING)
2978 OpenSurpriseBall(x, y);
2979 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_ICE)
2981 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_AMOEBA)
2985 AutoRotateMirrors();
2988 /* !!! CHANGE THIS: REDRAW ONLY WHEN NEEDED !!! */
2990 /* redraw after Explode_MM() ... */
2992 DrawLaser(0, DL_LASER_ENABLED);
2993 laser.redraw = FALSE;
2998 if (game_mm.num_pacman && FrameReached(&pacman_delay, PACMAN_MOVE_DELAY))
3002 if (laser.num_damages > MAX_LASER_LEN && !laser.fuse_off)
3004 DrawLaser(0, DL_LASER_DISABLED);
3009 if (FrameReached(&energy_delay, ENERGY_DELAY))
3011 game_mm.energy_left--;
3012 if (game_mm.energy_left >= 0)
3015 BlitBitmap(pix[PIX_DOOR], drawto,
3016 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
3017 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
3018 DX_ENERGY, DY_ENERGY);
3020 redraw_mask |= REDRAW_DOOR_1;
3022 else if (setup.time_limit)
3026 for (i = 15; i >= 0; i--)
3029 SetRGB(pen_ray, 0x0000, 0x0000, i * color_scale);
3031 pen_ray = GetPixelFromRGB(window,
3032 native_mm_level.laser_red * 0x11 * i,
3033 native_mm_level.laser_green * 0x11 * i,
3034 native_mm_level.laser_blue * 0x11 * i);
3036 DrawLaser(0, DL_LASER_ENABLED);
3041 StopSound_MM(SND_MM_GAME_HEALTH_CHARGING);
3044 DrawLaser(0, DL_LASER_DISABLED);
3045 game_mm.game_over = TRUE;
3046 game_mm.game_over_cause = GAME_OVER_NO_ENERGY;
3049 if (Request("Out of magic energy ! Play it again ?",
3050 REQ_ASK | REQ_STAY_CLOSED))
3056 game_status = MAINMENU;
3065 element = laser.dest_element;
3068 if (element != Feld[ELX][ELY])
3070 printf("element == %d, Feld[ELX][ELY] == %d\n",
3071 element, Feld[ELX][ELY]);
3075 if (!laser.overloaded && laser.overload_value == 0 &&
3076 element != EL_BOMB &&
3077 element != EL_MINE &&
3078 element != EL_BALL_GRAY &&
3079 element != EL_BLOCK_STONE &&
3080 element != EL_BLOCK_WOOD &&
3081 element != EL_FUSE_ON &&
3082 element != EL_FUEL_FULL &&
3083 !IS_WALL_ICE(element) &&
3084 !IS_WALL_AMOEBA(element))
3087 if (((laser.overloaded && laser.overload_value < MAX_LASER_OVERLOAD) ||
3088 (!laser.overloaded && laser.overload_value > 0)) &&
3089 FrameReached(&overload_delay, HEALTH_DELAY(laser.overloaded)))
3091 if (laser.overloaded)
3092 laser.overload_value++;
3094 laser.overload_value--;
3096 if (game_mm.cheat_no_overload)
3098 laser.overloaded = FALSE;
3099 laser.overload_value = 0;
3102 game_mm.laser_overload_value = laser.overload_value;
3104 if (laser.overload_value < MAX_LASER_OVERLOAD - 8)
3106 int color_up = 0xFF * laser.overload_value / MAX_LASER_OVERLOAD;
3107 int color_down = 0xFF - color_up;
3110 SetRGB(pen_ray, (laser.overload_value / 6) * color_scale, 0x0000,
3111 (15 - (laser.overload_value / 6)) * color_scale);
3114 GetPixelFromRGB(window,
3115 (native_mm_level.laser_red ? 0xFF : color_up),
3116 (native_mm_level.laser_green ? color_down : 0x00),
3117 (native_mm_level.laser_blue ? color_down : 0x00));
3119 DrawLaser(0, DL_LASER_ENABLED);
3125 if (!laser.overloaded)
3126 StopSound_MM(SND_MM_GAME_HEALTH_CHARGING);
3127 else if (setup.sound_loops)
3128 PlaySoundLoop_MM(SND_MM_GAME_HEALTH_CHARGING);
3130 PlaySound_MM(SND_MM_GAME_HEALTH_CHARGING);
3132 if (laser.overloaded)
3135 BlitBitmap(pix[PIX_DOOR], drawto,
3136 DOOR_GFX_PAGEX4 + XX_OVERLOAD,
3137 DOOR_GFX_PAGEY1 + YY_OVERLOAD + OVERLOAD_YSIZE
3138 - laser.overload_value,
3139 OVERLOAD_XSIZE, laser.overload_value,
3140 DX_OVERLOAD, DY_OVERLOAD + OVERLOAD_YSIZE
3141 - laser.overload_value);
3143 redraw_mask |= REDRAW_DOOR_1;
3148 BlitBitmap(pix[PIX_DOOR], drawto,
3149 DOOR_GFX_PAGEX5 + XX_OVERLOAD, DOOR_GFX_PAGEY1 + YY_OVERLOAD,
3150 OVERLOAD_XSIZE, OVERLOAD_YSIZE - laser.overload_value,
3151 DX_OVERLOAD, DY_OVERLOAD);
3153 redraw_mask |= REDRAW_DOOR_1;
3156 if (laser.overload_value == MAX_LASER_OVERLOAD)
3160 for (i = 15; i >= 0; i--)
3163 SetRGB(pen_ray, i * color_scale, 0x0000, 0x0000);
3166 pen_ray = GetPixelFromRGB(window, 0x11 * i, 0x00, 0x00);
3168 DrawLaser(0, DL_LASER_ENABLED);
3173 DrawLaser(0, DL_LASER_DISABLED);
3175 game_mm.game_over = TRUE;
3176 game_mm.game_over_cause = GAME_OVER_OVERLOADED;
3179 if (Request("Magic spell hit Mc Duffin ! Play it again ?",
3180 REQ_ASK | REQ_STAY_CLOSED))
3186 game_status = MAINMENU;
3200 if (element == EL_BOMB && CT > 75)
3202 if (game_mm.cheat_no_explosion)
3206 laser.num_damages--;
3207 DrawLaser(0, DL_LASER_DISABLED);
3208 laser.num_edges = 0;
3213 laser.dest_element = EL_EXPLODING_OPAQUE;
3217 laser.num_damages--;
3218 DrawLaser(0, DL_LASER_DISABLED);
3220 laser.num_edges = 0;
3221 Bang_MM(laser.start_edge.x, laser.start_edge.y);
3223 if (Request("Bomb killed Mc Duffin ! Play it again ?",
3224 REQ_ASK | REQ_STAY_CLOSED))
3230 game_status = MAINMENU;
3238 if (element == EL_FUSE_ON && CT > 25)
3240 laser.fuse_off = TRUE;
3244 DrawLaser(0, DL_LASER_DISABLED);
3245 DrawGraphic_MM(ELX, ELY, IMG_MM_FUSE);
3248 if (element == EL_BALL_GRAY && CT > 75)
3250 static int new_elements[] =
3253 EL_MIRROR_FIXED_START,
3255 EL_POLAR_CROSS_START,
3261 int num_new_elements = sizeof(new_elements) / sizeof(int);
3262 int new_element = new_elements[RND(num_new_elements)];
3264 Store[ELX][ELY] = new_element + RND(get_num_elements(new_element));
3265 Feld[ELX][ELY] = EL_GRAY_BALL_OPENING;
3267 /* !!! CHECK AGAIN: Laser on Polarizer !!! */
3278 element = EL_MIRROR_START + RND(16);
3284 element = (rnd == 0 ? EL_KETTLE : rnd == 1 ? EL_BOMB : EL_PRISM);
3291 element = (rnd == 0 ? EL_FUSE_ON :
3292 rnd >= 1 && rnd <= 4 ? EL_PACMAN_RIGHT + rnd - 1 :
3293 rnd >= 5 && rnd <= 20 ? EL_POLAR_START + rnd - 5 :
3294 rnd >= 21 && rnd <= 24 ? EL_POLAR_CROSS_START + rnd - 21 :
3295 EL_MIRROR_FIXED_START + rnd - 25);
3300 graphic = el2gfx(element);
3302 for (i = 0; i < 50; i++)
3308 BlitBitmap(pix[PIX_BACK], drawto,
3309 SX + (graphic % GFX_PER_LINE) * TILEX + x,
3310 SY + (graphic / GFX_PER_LINE) * TILEY + y, 6, 6,
3311 SX + ELX * TILEX + x,
3312 SY + ELY * TILEY + y);
3314 MarkTileDirty(ELX, ELY);
3317 DrawLaser(0, DL_LASER_ENABLED);
3322 Feld[ELX][ELY] = element;
3323 DrawField_MM(ELX, ELY);
3326 printf("NEW ELEMENT: (%d, %d)\n", ELX, ELY);
3329 /* above stuff: GRAY BALL -> PRISM !!! */
3331 LX = ELX * TILEX + 14;
3332 LY = ELY * TILEY + 14;
3333 if (laser.current_angle == (laser.current_angle >> 1) << 1)
3340 laser.num_edges -= 2;
3341 laser.num_damages--;
3345 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
3346 if (laser.damage[i].is_mirror)
3350 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3352 DrawLaser(0, DL_LASER_DISABLED);
3354 DrawLaser(0, DL_LASER_DISABLED);
3360 printf("TEST ELEMENT: %d\n", Feld[0][0]);
3367 if (IS_WALL_ICE(element) && CT > 50)
3369 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_SHRINKING);
3372 Feld[ELX][ELY] = Feld[ELX][ELY] - EL_WALL_ICE + EL_WALL_CHANGING;
3373 Store[ELX][ELY] = EL_WALL_ICE;
3374 Store2[ELX][ELY] = laser.wall_mask;
3376 laser.dest_element = Feld[ELX][ELY];
3381 for (i = 0; i < 5; i++)
3387 Feld[ELX][ELY] &= (laser.wall_mask ^ 0xFF);
3391 DrawWallsAnimation_MM(ELX, ELY, Feld[ELX][ELY], phase, laser.wall_mask);
3396 if (Feld[ELX][ELY] == EL_WALL_ICE)
3397 Feld[ELX][ELY] = EL_EMPTY;
3401 LX = laser.edge[laser.num_edges].x - (SX + 2);
3402 LY = laser.edge[laser.num_edges].y - (SY + 2);
3405 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
3406 if (laser.damage[i].is_mirror)
3410 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3412 DrawLaser(0, DL_LASER_DISABLED);
3419 if (IS_WALL_AMOEBA(element) && CT > 60)
3421 int k1, k2, k3, dx, dy, de, dm;
3422 int element2 = Feld[ELX][ELY];
3424 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element))
3427 for (i = laser.num_damages - 1; i >= 0; i--)
3428 if (laser.damage[i].is_mirror)
3431 r = laser.num_edges;
3432 d = laser.num_damages;
3439 DrawLaser(laser.damage[k1].edge - 1, DL_LASER_DISABLED);
3442 DrawLaser(0, DL_LASER_ENABLED);
3445 x = laser.damage[k1].x;
3446 y = laser.damage[k1].y;
3451 for (i = 0; i < 4; i++)
3453 if (laser.wall_mask & (1 << i))
3455 if (ReadPixel(drawto,
3456 SX + ELX * TILEX + 14 + (i % 2) * 2,
3457 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3459 if (ReadPixel(drawto,
3460 SX + ELX * TILEX + 31 * (i % 2),
3461 SY + ELY * TILEY + 14 + (i / 2) * 2) == pen_ray)
3468 for (i = 0; i < 4; i++)
3470 if (laser.wall_mask & (1 << i))
3472 if (ReadPixel(drawto,
3473 SX + ELX * TILEX + 31 * (i % 2),
3474 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3481 if (laser.num_beamers > 0 ||
3482 k1 < 1 || k2 < 4 || k3 < 4 ||
3483 ReadPixel(drawto, SX + ELX * TILEX + 14, SY + ELY * TILEY + 14)
3486 laser.num_edges = r;
3487 laser.num_damages = d;
3489 DrawLaser(0, DL_LASER_DISABLED);
3492 Feld[ELX][ELY] = element | laser.wall_mask;
3496 de = Feld[ELX][ELY];
3497 dm = laser.wall_mask;
3501 int x = ELX, y = ELY;
3502 int wall_mask = laser.wall_mask;
3505 DrawLaser(0, DL_LASER_ENABLED);
3507 PlayLevelSound_MM(dx, dy, element, MM_ACTION_GROWING);
3509 Feld[x][y] = Feld[x][y] - EL_WALL_AMOEBA + EL_WALL_CHANGING;
3510 Store[x][y] = EL_WALL_AMOEBA;
3511 Store2[x][y] = wall_mask;
3517 DrawWallsAnimation_MM(dx, dy, de, 4, dm);
3519 DrawLaser(0, DL_LASER_ENABLED);
3521 PlayLevelSound_MM(dx, dy, element, MM_ACTION_GROWING);
3523 for (i = 4; i >= 0; i--)
3525 DrawWallsAnimation_MM(dx, dy, de, i, dm);
3531 DrawLaser(0, DL_LASER_ENABLED);
3536 if ((element == EL_BLOCK_WOOD || element == EL_BLOCK_STONE) &&
3537 laser.stops_inside_element && CT > 75)
3542 if (ABS(XS) > ABS(YS))
3549 for (i = 0; i < 4; i++)
3556 x = ELX + Step[k * 4].x;
3557 y = ELY + Step[k * 4].y;
3559 if (!IN_LEV_FIELD(x, y) || Feld[x][y] != EL_EMPTY)
3562 if (ObjHit(x, y, HIT_POS_CENTER | HIT_POS_EDGE | HIT_POS_BETWEEN))
3570 laser.overloaded = (element == EL_BLOCK_STONE);
3575 PlayLevelSound_MM(ELX, ELY, element, MM_ACTION_PUSHING);
3578 Feld[x][y] = element;
3580 DrawGraphic_MM(ELX, ELY, IMG_EMPTY);
3583 if (element == EL_BLOCK_STONE && Box[ELX][ELY])
3585 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
3586 DrawLaser(laser.num_edges - 1, DL_LASER_ENABLED);
3594 if (element == EL_FUEL_FULL && CT > 10)
3596 for (i = game_mm.energy_left; i <= MAX_LASER_ENERGY; i+=2)
3599 BlitBitmap(pix[PIX_DOOR], drawto,
3600 DOOR_GFX_PAGEX4 + XX_ENERGY,
3601 DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
3602 ENERGY_XSIZE, i, DX_ENERGY,
3603 DY_ENERGY + ENERGY_YSIZE - i);
3606 redraw_mask |= REDRAW_DOOR_1;
3612 game_mm.energy_left = MAX_LASER_ENERGY;
3613 Feld[ELX][ELY] = EL_FUEL_EMPTY;
3614 DrawField_MM(ELX, ELY);
3616 DrawLaser(0, DL_LASER_ENABLED);
3624 void GameActions_MM(struct MouseActionInfo action, boolean warp_mode)
3626 ClickElement(action.lx, action.ly, action.button);
3628 GameActions_MM_Ext(action, warp_mode);
3634 int mx, my, ox, oy, nx, ny;
3638 if (++p >= game_mm.num_pacman)
3641 game_mm.pacman[p].dir--;
3643 for (l = 1; l < 5; l++)
3645 game_mm.pacman[p].dir++;
3647 if (game_mm.pacman[p].dir > 4)
3648 game_mm.pacman[p].dir = 1;
3650 if (game_mm.pacman[p].dir % 2)
3653 my = game_mm.pacman[p].dir - 2;
3658 mx = 3 - game_mm.pacman[p].dir;
3661 ox = game_mm.pacman[p].x;
3662 oy = game_mm.pacman[p].y;
3665 element = Feld[nx][ny];
3667 if (nx < 0 || nx > 15 || ny < 0 || ny > 11)
3670 if (!IS_EATABLE4PACMAN(element))
3673 if (ObjHit(nx, ny, HIT_POS_CENTER))
3676 Feld[ox][oy] = EL_EMPTY;
3678 EL_PACMAN_RIGHT - 1 +
3679 (game_mm.pacman[p].dir - 1 +
3680 (game_mm.pacman[p].dir % 2) * 2);
3682 game_mm.pacman[p].x = nx;
3683 game_mm.pacman[p].y = ny;
3685 DrawGraphic_MM(ox, oy, IMG_EMPTY);
3687 if (element != EL_EMPTY)
3689 int graphic = el2gfx(Feld[nx][ny]);
3694 getGraphicSource(graphic, 0, &bitmap, &src_x, &src_y);
3697 ox = SX + ox * TILEX;
3698 oy = SY + oy * TILEY;
3700 for (i = 1; i < 33; i += 2)
3701 BlitBitmap(bitmap, window,
3702 src_x, src_y, TILEX, TILEY,
3703 ox + i * mx, oy + i * my);
3704 Ct = Ct + FrameCounter - CT;
3707 DrawField_MM(nx, ny);
3710 if (!laser.fuse_off)
3712 DrawLaser(0, DL_LASER_ENABLED);
3714 if (ObjHit(nx, ny, HIT_POS_BETWEEN))
3716 AddDamagedField(nx, ny);
3718 laser.damage[laser.num_damages - 1].edge = 0;
3722 if (element == EL_BOMB)
3723 DeletePacMan(nx, ny);
3725 if (IS_WALL_AMOEBA(element) &&
3726 (LX + 2 * XS) / TILEX == nx &&
3727 (LY + 2 * YS) / TILEY == ny)
3740 boolean raise_level = FALSE;
3743 if (local_player->MovPos)
3746 local_player->LevelSolved = FALSE;
3749 if (game_mm.energy_left)
3751 if (setup.sound_loops)
3752 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3753 SND_CTRL_PLAY_LOOP);
3755 while (game_mm.energy_left > 0)
3757 if (!setup.sound_loops)
3758 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3761 if (game_mm.energy_left > 0 && !(game_mm.energy_left % 10))
3762 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3767 game_mm.energy_left--;
3768 if (game_mm.energy_left >= 0)
3771 BlitBitmap(pix[PIX_DOOR], drawto,
3772 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
3773 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
3774 DX_ENERGY, DY_ENERGY);
3776 redraw_mask |= REDRAW_DOOR_1;
3783 if (setup.sound_loops)
3784 StopSound(SND_SIRR);
3786 else if (native_mm_level.time == 0) /* level without time limit */
3788 if (setup.sound_loops)
3789 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3790 SND_CTRL_PLAY_LOOP);
3792 while (TimePlayed < 999)
3794 if (!setup.sound_loops)
3795 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3796 if (TimePlayed < 999 && !(TimePlayed % 10))
3797 RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3798 if (TimePlayed < 900 && !(TimePlayed % 10))
3804 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
3811 if (setup.sound_loops)
3812 StopSound(SND_SIRR);
3819 CloseDoor(DOOR_CLOSE_1);
3821 Request("Level solved !", REQ_CONFIRM);
3823 if (level_nr == leveldir_current->handicap_level)
3825 leveldir_current->handicap_level++;
3826 SaveLevelSetup_SeriesInfo();
3829 if (level_editor_test_game)
3830 game_mm.score = -1; /* no highscore when playing from editor */
3831 else if (level_nr < leveldir_current->last_level)
3832 raise_level = TRUE; /* advance to next level */
3834 if ((hi_pos = NewHiScore_MM()) >= 0)
3836 game_status = HALLOFFAME;
3838 // DrawHallOfFame(hi_pos);
3845 game_status = MAINMENU;
3861 // LoadScore(level_nr);
3863 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
3864 game_mm.score < highscore[MAX_SCORE_ENTRIES - 1].Score)
3867 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3869 if (game_mm.score > highscore[k].Score)
3871 /* player has made it to the hall of fame */
3873 if (k < MAX_SCORE_ENTRIES - 1)
3875 int m = MAX_SCORE_ENTRIES - 1;
3878 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3879 if (!strcmp(setup.player_name, highscore[l].Name))
3881 if (m == k) /* player's new highscore overwrites his old one */
3885 for (l = m; l>k; l--)
3887 strcpy(highscore[l].Name, highscore[l - 1].Name);
3888 highscore[l].Score = highscore[l - 1].Score;
3895 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3896 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3897 highscore[k].Score = game_mm.score;
3904 else if (!strncmp(setup.player_name, highscore[k].Name,
3905 MAX_PLAYER_NAME_LEN))
3906 break; /* player already there with a higher score */
3911 // if (position >= 0)
3912 // SaveScore(level_nr);
3917 static void InitMovingField_MM(int x, int y, int direction)
3919 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3920 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3922 MovDir[x][y] = direction;
3923 MovDir[newx][newy] = direction;
3925 if (Feld[newx][newy] == EL_EMPTY)
3926 Feld[newx][newy] = EL_BLOCKED;
3929 static void Moving2Blocked_MM(int x, int y, int *goes_to_x, int *goes_to_y)
3931 int direction = MovDir[x][y];
3932 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3933 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3939 static void Blocked2Moving_MM(int x, int y,
3940 int *comes_from_x, int *comes_from_y)
3942 int oldx = x, oldy = y;
3943 int direction = MovDir[x][y];
3945 if (direction == MV_LEFT)
3947 else if (direction == MV_RIGHT)
3949 else if (direction == MV_UP)
3951 else if (direction == MV_DOWN)
3954 *comes_from_x = oldx;
3955 *comes_from_y = oldy;
3958 static int MovingOrBlocked2Element_MM(int x, int y)
3960 int element = Feld[x][y];
3962 if (element == EL_BLOCKED)
3966 Blocked2Moving_MM(x, y, &oldx, &oldy);
3968 return Feld[oldx][oldy];
3975 static void RemoveField(int x, int y)
3977 Feld[x][y] = EL_EMPTY;
3984 static void RemoveMovingField_MM(int x, int y)
3986 int oldx = x, oldy = y, newx = x, newy = y;
3988 if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
3991 if (IS_MOVING(x, y))
3993 Moving2Blocked_MM(x, y, &newx, &newy);
3994 if (Feld[newx][newy] != EL_BLOCKED)
3997 else if (Feld[x][y] == EL_BLOCKED)
3999 Blocked2Moving_MM(x, y, &oldx, &oldy);
4000 if (!IS_MOVING(oldx, oldy))
4004 Feld[oldx][oldy] = EL_EMPTY;
4005 Feld[newx][newy] = EL_EMPTY;
4006 MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
4007 MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
4009 DrawLevelField_MM(oldx, oldy);
4010 DrawLevelField_MM(newx, newy);
4013 void PlaySoundLevel(int x, int y, int sound_nr)
4015 int sx = SCREENX(x), sy = SCREENY(y);
4017 int silence_distance = 8;
4019 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
4020 (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
4023 if (!IN_LEV_FIELD(x, y) ||
4024 sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
4025 sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
4028 volume = SOUND_MAX_VOLUME;
4031 stereo = (sx - SCR_FIELDX/2) * 12;
4033 stereo = SOUND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
4034 if (stereo > SOUND_MAX_RIGHT)
4035 stereo = SOUND_MAX_RIGHT;
4036 if (stereo < SOUND_MAX_LEFT)
4037 stereo = SOUND_MAX_LEFT;
4040 if (!IN_SCR_FIELD(sx, sy))
4042 int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
4043 int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
4045 volume -= volume * (dx > dy ? dx : dy) / silence_distance;
4048 PlaySoundExt(sound_nr, volume, stereo, SND_CTRL_PLAY_SOUND);
4051 static void RaiseScore_MM(int value)
4053 game_mm.score += value;
4056 DrawText(DX_SCORE, DY_SCORE, int2str(game_mm.score, 4),
4061 void RaiseScoreElement_MM(int element)
4066 RaiseScore_MM(native_mm_level.score[SC_PACMAN]);
4070 RaiseScore_MM(native_mm_level.score[SC_KEY]);