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() */
25 #define EX_PHASE_START 0
30 /* special positions in the game control window (relative to control window) */
39 #define XX_OVERLOAD 60
40 #define YY_OVERLOAD YY_ENERGY
42 /* special positions in the game control window (relative to main window) */
43 #define DX_LEVEL (DX + XX_LEVEL)
44 #define DY_LEVEL (DY + YY_LEVEL)
45 #define DX_KETTLES (DX + XX_KETTLES)
46 #define DY_KETTLES (DY + YY_KETTLES)
47 #define DX_SCORE (DX + XX_SCORE)
48 #define DY_SCORE (DY + YY_SCORE)
49 #define DX_ENERGY (DX + XX_ENERGY)
50 #define DY_ENERGY (DY + YY_ENERGY)
51 #define DX_OVERLOAD (DX + XX_OVERLOAD)
52 #define DY_OVERLOAD (DY + YY_OVERLOAD)
54 #define IS_LOOP_SOUND(s) ((s) == SND_FUEL)
55 #define IS_MUSIC_SOUND(s) ((s) == SND_TYGER || (s) == SND_VOYAGER)
57 /* game button identifiers */
58 #define GAME_CTRL_ID_LEFT 0
59 #define GAME_CTRL_ID_MIDDLE 1
60 #define GAME_CTRL_ID_RIGHT 2
62 #define NUM_GAME_BUTTONS 3
64 /* values for DrawLaser() */
65 #define DL_LASER_DISABLED 0
66 #define DL_LASER_ENABLED 1
68 /* values for 'click_delay_value' in ClickElement() */
69 #define CLICK_DELAY_SHORT 125
70 #define CLICK_DELAY_LONG 250
71 #define AUTO_ROTATE_DELAY CLICK_DELAY_SHORT
73 /* forward declaration for internal use */
76 void GetPlayerConfig()
78 if (!audio.sound_available)
81 if (!audio.loops_available)
83 setup.sound_loops = FALSE;
84 setup.sound_music = FALSE;
87 if (!video.fullscreen_available)
88 setup.fullscreen = FALSE;
90 setup.sound_simple = setup.sound;
92 SetAudioMode(setup.sound);
95 static int get_element_angle(int element)
97 int element_phase = get_element_phase(element);
99 if (IS_MIRROR_FIXED(element) ||
100 IS_MCDUFFIN(element) ||
102 IS_RECEIVER(element))
103 return 4 * element_phase;
105 return element_phase;
108 static int get_opposite_angle(int angle)
110 int opposite_angle = angle + ANG_RAY_180;
112 /* make sure "opposite_angle" is in valid interval [0, 15] */
113 return (opposite_angle + 16) % 16;
116 static int get_mirrored_angle(int laser_angle, int mirror_angle)
118 int reflected_angle = 16 - laser_angle + mirror_angle;
120 /* make sure "reflected_angle" is in valid interval [0, 15] */
121 return (reflected_angle + 16) % 16;
124 void InitMovDir(int x, int y)
126 int element = Feld[x][y];
127 static int direction[3][4] =
129 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
130 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
131 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
136 case EL_PACMAN_RIGHT:
140 Feld[x][y] = EL_PACMAN;
141 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
149 static void InitField(int x, int y, boolean init_game)
151 int element = Feld[x][y];
156 Feld[x][y] = EL_EMPTY;
161 if (native_mm_level.auto_count_kettles)
162 game_mm.kettles_still_needed++;
165 case EL_LIGHTBULB_OFF:
166 game_mm.lights_still_needed++;
170 if (IS_MIRROR(element) ||
171 IS_BEAMER_OLD(element) ||
172 IS_BEAMER(element) ||
174 IS_POLAR_CROSS(element) ||
175 IS_DF_MIRROR(element) ||
176 IS_DF_MIRROR_AUTO(element) ||
177 IS_GRID_STEEL_AUTO(element) ||
178 IS_GRID_WOOD_AUTO(element) ||
179 IS_FIBRE_OPTIC(element))
181 if (IS_BEAMER_OLD(element))
183 Feld[x][y] = EL_BEAMER_BLUE_START + (element - EL_BEAMER_START);
184 element = Feld[x][y];
187 if (!IS_FIBRE_OPTIC(element))
189 static int steps_grid_auto = 0;
191 if (game_mm.num_cycle == 0) /* initialize cycle steps for grids */
192 steps_grid_auto = RND(16) * (RND(2) ? -1 : +1);
194 if (IS_GRID_STEEL_AUTO(element) ||
195 IS_GRID_WOOD_AUTO(element))
196 game_mm.cycle[game_mm.num_cycle].steps = steps_grid_auto;
198 game_mm.cycle[game_mm.num_cycle].steps = RND(16) * (RND(2) ? -1 : +1);
200 game_mm.cycle[game_mm.num_cycle].x = x;
201 game_mm.cycle[game_mm.num_cycle].y = y;
205 if (IS_BEAMER(element) || IS_FIBRE_OPTIC(element))
207 int beamer_nr = BEAMER_NR(element);
208 int nr = laser.beamer[beamer_nr][0].num;
210 laser.beamer[beamer_nr][nr].x = x;
211 laser.beamer[beamer_nr][nr].y = y;
212 laser.beamer[beamer_nr][nr].num = 1;
215 else if (IS_PACMAN(element))
218 int phase = element - EL_PACMAN_RIGHT;
220 game_mm.pacman[game_mm.num_pacman].x = x;
221 game_mm.pacman[game_mm.num_pacman].y = y;
222 game_mm.pacman[game_mm.num_pacman].dir = phase + ((phase + 1) % 2) * 2;
223 game_mm.num_pacman++;
228 else if (IS_MCDUFFIN(element) || IS_LASER(element))
230 laser.start_edge.x = x;
231 laser.start_edge.y = y;
232 laser.start_angle = get_element_angle(element);
239 static void InitCycleElements()
243 if (game_mm.num_cycle == 0) /* no elements to cycle */
248 for(j=0; j<game_mm.num_cycle; j++)
250 int x = game_mm.cycle[j].x;
251 int y = game_mm.cycle[j].y;
252 int step = SIGN(game_mm.cycle[j].steps);
253 int last_element = Feld[x][y];
254 int next_element = get_rotated_element(last_element, step);
256 if (!game_mm.cycle[j].steps)
259 Feld[x][y] = next_element;
262 game_mm.cycle[j].steps -= step;
269 if (setup.quick_doors)
273 Delay(AUTO_ROTATE_DELAY);
277 static void InitLaser()
279 int start_element = Feld[laser.start_edge.x][laser.start_edge.y];
280 int step = (IS_LASER(start_element) ? 4 : 0);
282 LX = laser.start_edge.x * TILEX;
283 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
286 LX += (laser.start_angle == ANG_RAY_RIGHT ? 28 + step : 0 - step);
288 LY = laser.start_edge.y * TILEY;
289 if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
290 LY += (laser.start_angle == ANG_RAY_DOWN ? 28 + step : 0 - step);
294 XS = 2 * Step[laser.start_angle].x;
295 YS = 2 * Step[laser.start_angle].y;
297 laser.current_angle = laser.start_angle;
299 laser.num_damages = 0;
301 laser.num_beamers = 0;
302 laser.beamer_edge[0] = 0;
304 AddLaserEdge(LX, LY); /* set laser starting edge */
306 pen_ray = GetPixelFromRGB(window,
307 native_mm_level.laser_red * 0xFF,
308 native_mm_level.laser_green * 0xFF,
309 native_mm_level.laser_blue * 0xFF);
316 /* set global editor control values */
317 editor.draw_walls_masked = FALSE;
319 /* set global game control values */
320 game_mm.num_cycle = 0;
321 game_mm.num_pacman = 0;
324 game_mm.energy_left = native_mm_level.time;
325 game_mm.kettles_still_needed =
326 (native_mm_level.auto_count_kettles ? 0 : native_mm_level.kettles_needed);
327 game_mm.lights_still_needed = 0;
328 game_mm.num_keys = 0;
330 game_mm.level_solved = FALSE;
331 game_mm.game_over = FALSE;
332 game_mm.game_over_cause = 0;
334 /* set global laser control values (must be set before "InitLaser()") */
335 laser.start_edge.x = 0;
336 laser.start_edge.y = 0;
337 laser.start_angle = 0;
339 for (i=0; i<MAX_NUM_BEAMERS; i++)
340 laser.beamer[i][0].num = laser.beamer[i][1].num = 0;
342 laser.overloaded = FALSE;
343 laser.overload_value = 0;
344 laser.fuse_off = FALSE;
345 laser.fuse_x = laser.fuse_y = -1;
347 laser.dest_element = EL_EMPTY;
352 for (x=0; x<lev_fieldx; x++)
354 for (y=0; y<lev_fieldy; y++)
356 Feld[x][y] = Ur[x][y];
357 Hit[x][y] = Box[x][y] = 0;
359 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
360 Store[x][y] = Store2[x][y] = 0;
364 InitField(x, y, TRUE);
368 CloseDoor(DOOR_CLOSE_1);
374 /* copy default game door content to main double buffer */
375 BlitBitmap(pix[PIX_DOOR], drawto,
376 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
379 DrawText(DX_LEVEL, DY_LEVEL,
380 int2str(level_nr, 2), FONT_TEXT_2);
381 DrawText(DX_KETTLES, DY_KETTLES,
382 int2str(game_mm.kettles_still_needed, 3), FONT_TEXT_2);
383 DrawText(DX_SCORE, DY_SCORE,
384 int2str(game_mm.score, 4), FONT_TEXT_2);
389 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
390 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
391 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
395 /* copy actual game door content to door double buffer for OpenDoor() */
396 BlitBitmap(drawto, pix[PIX_DB_DOOR],
397 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
399 OpenDoor(DOOR_OPEN_ALL);
401 if (setup.sound_loops)
402 PlaySoundExt(SND_FUEL, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT, SND_CTRL_PLAY_LOOP);
404 for(i=0; i<=game_mm.energy_left; i+=2)
406 if (!setup.sound_loops)
407 PlaySoundStereo(SND_FUEL, SOUND_MAX_RIGHT);
409 BlitBitmap(pix[PIX_DOOR], drawto,
410 DOOR_GFX_PAGEX4 + XX_ENERGY,
411 DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
413 DX_ENERGY, DY_ENERGY + ENERGY_YSIZE - i);
415 redraw_mask |= REDRAW_DOOR_1;
421 if (setup.quick_doors)
428 if (setup.sound_loops)
431 if (setup.sound_music && num_bg_loops)
432 PlayMusic(level_nr % num_bg_loops);
437 void AddLaserEdge(int lx, int ly)
439 if (lx < -2 || ly < -2 || lx >= SXSIZE + 2 || ly >= SYSIZE + 2)
441 Error(ERR_WARN, "AddLaserEdge: out of bounds: %d, %d", lx, ly);
445 laser.edge[laser.num_edges].x = SX + 2 + lx;
446 laser.edge[laser.num_edges].y = SY + 2 + ly;
452 void AddDamagedField(int ex, int ey)
454 laser.damage[laser.num_damages].is_mirror = FALSE;
455 laser.damage[laser.num_damages].angle = laser.current_angle;
456 laser.damage[laser.num_damages].edge = laser.num_edges;
457 laser.damage[laser.num_damages].x = ex;
458 laser.damage[laser.num_damages].y = ey;
468 int last_x = laser.edge[laser.num_edges - 1].x - SX - 2;
469 int last_y = laser.edge[laser.num_edges - 1].y - SY - 2;
471 return ((x - last_x) * XS < 0 || (y - last_y) * YS < 0);
477 static int getMaskFromElement(int element)
479 if (IS_GRID(element))
480 return GFX_MASK_GRID_00 + get_element_phase(element);
481 else if (IS_MCDUFFIN(element))
482 return GFX_MASK_MCDUFFIN_00 + get_element_phase(element);
483 else if (IS_RECTANGLE(element) || IS_DF_GRID(element))
484 return GFX_MASK_RECTANGLE;
486 return GFX_MASK_CIRCLE;
494 printf("ScanPixel: start scanning at (%d, %d) [%d, %d] [%d, %d]\n",
495 LX, LY, LX / TILEX, LY / TILEY, LX % TILEX, LY % TILEY);
498 /* follow laser beam until it hits something (at least the screen border) */
499 while (hit_mask == HIT_MASK_NO_HIT)
505 if (SX + LX < REAL_SX || SX + LX >= REAL_SX + FULL_SXSIZE ||
506 SY + LY < REAL_SY || SY + LY >= REAL_SY + FULL_SYSIZE)
508 printf("ScanPixel: touched screen border!\n");
519 px = SX + LX + (i % 2) * 2;
520 py = SY + LY + (i / 2) * 2;
521 lx = (px - SX + TILEX) / TILEX - 1; /* ...+TILEX...-1 to get correct */
522 ly = (py - SY + TILEY) / TILEY - 1; /* negative values! */
524 if (IN_LEV_FIELD(lx, ly))
526 int element = Feld[lx][ly];
528 if (element == EL_EMPTY || element == EL_EXPLODING_TRANSP)
530 else if (IS_WALL(element) || IS_WALL_CHANGING(element))
533 ((py - SY - ly * TILEY) / MINI_TILEX) * 2 +
534 (px - SX - lx * TILEX) / MINI_TILEY;
536 pixel = ((element & (1 << pos)) ? 1 : 0);
540 int graphic_mask = getMaskFromElement(element);
542 int dx = px - lx * TILEX;
543 int dy = py - ly * TILEY;
545 mask_x = (graphic_mask % GFX_PER_LINE) * TILEX + dx;
546 mask_y = (graphic_mask / GFX_PER_LINE) * TILEY + dy;
548 pixel = (ReadPixel(pix[PIX_BACK], mask_x, mask_y) ? 1 : 0);
553 if (px < REAL_SX || px >= REAL_SX + FULL_SXSIZE ||
554 py < REAL_SY || py >= REAL_SY + FULL_SYSIZE)
560 if ((Sign[laser.current_angle] & (1 << i)) && pixel)
561 hit_mask |= (1 << i);
564 if (hit_mask == HIT_MASK_NO_HIT)
566 /* hit nothing -- go on with another step */
578 int end = 0, rf = laser.num_edges;
580 unsigned short color_scale = 0xFFFF / 15;
586 laser.overloaded = FALSE;
587 laser.stops_inside_element = FALSE;
590 if (laser.overload_value < MAX_LASER_OVERLOAD - 8)
592 (laser.overload_value / 6) * color_scale, 0x0000,
593 (15 - (laser.overload_value / 6)) * color_scale);
596 DrawLaser(0, DL_LASER_ENABLED);
599 printf("Start scanning with LX == %d, LY == %d, XS == %d, YS == %d\n",
607 if (laser.num_edges > MAX_LASER_LEN || laser.num_damages > MAX_LASER_LEN)
610 laser.overloaded = TRUE;
614 hit_mask = ScanPixel();
617 printf("Hit something at LX == %d, LY == %d, XS == %d, YS == %d\n",
621 /* hit something -- check out what it was */
622 ELX = (LX + XS) / TILEX;
623 ELY = (LY + YS) / TILEY;
626 printf("hit_mask (1) == '%x' (%d, %d) (%d, %d)\n",
627 hit_mask, LX, LY, ELX, ELY);
630 if (!IN_LEV_FIELD(ELX, ELY) || !IN_PIX_FIELD(LX, LY))
633 laser.dest_element = element;
638 if (hit_mask == (HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT))
640 /* we have hit the top-right and bottom-left element --
641 choose the bottom-left one */
642 /* !!! THIS CAN BE DONE MORE INTELLIGENTLY, FOR EXAMPLE, IF ONE
643 ELEMENT WAS STEEL AND THE OTHER ONE WAS ICE => ALWAYS CHOOSE
644 THE ICE AND MELT IT AWAY INSTEAD OF OVERLOADING LASER !!! */
645 ELX = (LX - 2) / TILEX;
646 ELY = (LY + 2) / TILEY;
649 if (hit_mask == (HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT))
651 /* we have hit the top-left and bottom-right element --
652 choose the top-left one */
653 /* !!! SEE ABOVE !!! */
654 ELX = (LX - 2) / TILEX;
655 ELY = (LY - 2) / TILEY;
660 printf("hit_mask (2) == '%x' (%d, %d) (%d, %d)\n",
661 hit_mask, LX, LY, ELX, ELY);
664 element = Feld[ELX][ELY];
665 laser.dest_element = element;
668 printf("Hit element %d at (%d, %d) [%d, %d] [%d, %d] [%d]\n",
671 LX % TILEX, LY % TILEY,
676 if (!IN_LEV_FIELD(ELX, ELY))
677 printf("WARNING! (1) %d, %d (%d)\n", ELX, ELY, element);
685 if (element == EL_EMPTY)
687 if (!HitOnlyAnEdge(element, hit_mask))
690 else if (element == EL_FUSE_ON)
692 if (HitPolarizer(element, hit_mask))
695 else if (IS_GRID(element) || IS_DF_GRID(element))
697 if (HitPolarizer(element, hit_mask))
700 else if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD ||
701 element == EL_GATE_STONE || element == EL_GATE_WOOD)
703 if (HitBlock(element, hit_mask))
709 else if (IS_MCDUFFIN(element))
711 if (HitLaserSource(element, hit_mask))
714 else if ((element >= EL_EXIT_CLOSED && element <= EL_EXIT_OPEN) ||
715 IS_RECEIVER(element))
717 if (HitLaserDestination(element, hit_mask))
720 else if (IS_WALL(element))
722 if (IS_WALL_STEEL(element) || IS_DF_WALL_STEEL(element))
724 if (HitReflectingWalls(element, hit_mask))
729 if (HitAbsorbingWalls(element, hit_mask))
735 if (HitElement(element, hit_mask))
740 DrawLaser(rf - 1, DL_LASER_ENABLED);
741 rf = laser.num_edges;
745 element = Feld[ELX][ELY];
746 laser.dest_element = element;
753 if (laser.dest_element != Feld[ELX][ELY])
755 printf("ALARM: laser.dest_element == %d, Feld[ELX][ELY] == %d\n",
756 laser.dest_element, Feld[ELX][ELY]);
761 if (!end && !laser.stops_inside_element && !StepBehind())
764 printf("ScanLaser: Go one step back\n");
769 AddLaserEdge(LX, LY);
773 DrawLaser(rf - 1, DL_LASER_ENABLED);
779 if (!IN_LEV_FIELD(ELX, ELY))
780 printf("WARNING! (2) %d, %d\n", ELX, ELY);
785 printf("(%d, %d) == %d [(%d, %d) == %d]\n",
786 testx, testy, laser.dest_element,
787 ELX, ELY, (IN_SCR_FIELD(ELX,ELY) ? Feld[ELX][ELY] : -1));
792 void DrawLaserExt(int start_edge, int num_edges, int mode)
798 printf("DrawLaserExt: start_edge, num_edges, mode == %d, %d, %d\n",
799 start_edge, num_edges, mode);
804 Error(ERR_WARN, "DrawLaserExt: start_edge < 0");
810 Error(ERR_WARN, "DrawLaserExt: num_edges < 0");
815 if (mode == DL_LASER_DISABLED)
817 printf("DrawLaser: Delete laser from edge %d\n", start_edge);
821 /* now draw the laser to the backbuffer and (if enabled) to the screen */
822 DrawLines(drawto, &laser.edge[start_edge], num_edges,
823 (mode == DL_LASER_ENABLED ? pen_ray : pen_bg));
825 redraw_mask |= REDRAW_FIELD;
827 if (mode == DL_LASER_ENABLED)
830 /* after the laser was deleted, the "damaged" graphics must be restored */
831 if (laser.num_damages)
833 int damage_start = 0;
836 /* determine the starting edge, from which graphics need to be restored */
839 for(i=0; i<laser.num_damages; i++)
841 if (laser.damage[i].edge == start_edge + 1)
849 /* restore graphics from this starting edge to the end of damage list */
850 for(i=damage_start; i<laser.num_damages; i++)
852 int lx = laser.damage[i].x;
853 int ly = laser.damage[i].y;
854 int element = Feld[lx][ly];
856 if (Hit[lx][ly] == laser.damage[i].edge)
857 if (!((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
860 if (Box[lx][ly] == laser.damage[i].edge)
863 if (IS_DRAWABLE(element))
864 DrawField_MM(lx, ly);
867 elx = laser.damage[damage_start].x;
868 ely = laser.damage[damage_start].y;
869 element = Feld[elx][ely];
873 if (IS_BEAMER(element))
877 for (i=0; i<laser.num_beamers; i++)
878 printf("-> %d\n", laser.beamer_edge[i]);
879 printf("DrawLaserExt: IS_BEAMER: [%d]: Hit[%d][%d] == %d [%d]\n",
880 mode, elx, ely, Hit[elx][ely], start_edge);
881 printf("DrawLaserExt: IS_BEAMER: %d / %d\n",
882 get_element_angle(element), laser.damage[damage_start].angle);
886 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
887 laser.num_beamers > 0 &&
888 start_edge == laser.beamer_edge[laser.num_beamers - 1])
890 /* element is outgoing beamer */
891 laser.num_damages = damage_start + 1;
892 if (IS_BEAMER(element))
893 laser.current_angle = get_element_angle(element);
897 /* element is incoming beamer or other element */
898 laser.num_damages = damage_start;
899 laser.current_angle = laser.damage[laser.num_damages].angle;
904 /* no damages but McDuffin himself (who needs to be redrawn anyway) */
906 elx = laser.start_edge.x;
907 ely = laser.start_edge.y;
908 element = Feld[elx][ely];
911 laser.num_edges = start_edge + 1;
913 laser.current_angle = laser.start_angle;
914 LX = laser.edge[start_edge].x - (SX + 2);
915 LY = laser.edge[start_edge].y - (SY + 2);
916 XS = 2 * Step[laser.current_angle].x;
917 YS = 2 * Step[laser.current_angle].y;
920 printf("DrawLaser: Set (LX, LY) to (%d, %d) [%d]\n",
926 if (IS_BEAMER(element) ||
927 IS_FIBRE_OPTIC(element) ||
928 IS_PACMAN(element) ||
930 IS_POLAR_CROSS(element) ||
931 element == EL_FUSE_ON)
936 printf("element == %d\n", element);
939 if (IS_22_5_ANGLE(laser.current_angle)) /* neither 90° nor 45° angle */
940 step_size = ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) ? 4 : 3);
944 if (IS_POLAR(element) || IS_POLAR_CROSS(element) ||
945 ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
946 (laser.num_beamers == 0 ||
947 start_edge != laser.beamer_edge[laser.num_beamers - 1])))
949 /* element is incoming beamer or other element */
950 step_size = -step_size;
955 if (IS_BEAMER(element))
957 printf("start_edge == %d, laser.beamer_edge == %d\n",
958 start_edge, laser.beamer_edge);
962 LX += step_size * XS;
963 LY += step_size * YS;
965 else if (element != EL_EMPTY)
974 printf("DrawLaser: Finally: (LX, LY) to (%d, %d) [%d]\n",
979 void DrawLaser(int start_edge, int mode)
981 if (laser.num_edges - start_edge < 0)
983 Error(ERR_WARN, "DrawLaser: laser.num_edges - start_edge < 0");
987 /* check if laser is interrupted by beamer element */
988 if (laser.num_beamers > 0 &&
989 start_edge < laser.beamer_edge[laser.num_beamers - 1])
991 if (mode == DL_LASER_ENABLED)
994 int tmp_start_edge = start_edge;
996 /* draw laser segments forward from the start to the last beamer */
997 for (i=0; i<laser.num_beamers; i++)
999 int tmp_num_edges = laser.beamer_edge[i] - tmp_start_edge;
1001 if (tmp_num_edges <= 0)
1005 printf("DrawLaser: DL_LASER_ENABLED: i==%d: %d, %d\n",
1006 i, laser.beamer_edge[i], tmp_start_edge);
1009 DrawLaserExt(tmp_start_edge, tmp_num_edges, DL_LASER_ENABLED);
1010 tmp_start_edge = laser.beamer_edge[i];
1013 /* draw last segment from last beamer to the end */
1014 DrawLaserExt(tmp_start_edge, laser.num_edges - tmp_start_edge,
1020 int last_num_edges = laser.num_edges;
1021 int num_beamers = laser.num_beamers;
1023 /* delete laser segments backward from the end to the first beamer */
1024 for (i=num_beamers-1; i>=0; i--)
1026 int tmp_num_edges = last_num_edges - laser.beamer_edge[i];
1028 if (laser.beamer_edge[i] - start_edge <= 0)
1031 DrawLaserExt(laser.beamer_edge[i], tmp_num_edges, DL_LASER_DISABLED);
1032 last_num_edges = laser.beamer_edge[i];
1033 laser.num_beamers--;
1037 if (last_num_edges - start_edge <= 0)
1038 printf("DrawLaser: DL_LASER_DISABLED: %d, %d\n",
1039 last_num_edges, start_edge);
1042 /* delete first segment from start to the first beamer */
1043 DrawLaserExt(start_edge, last_num_edges - start_edge, DL_LASER_DISABLED);
1047 DrawLaserExt(start_edge, laser.num_edges - start_edge, mode);
1050 boolean HitElement(int element, int hit_mask)
1052 if (HitOnlyAnEdge(element, hit_mask))
1055 if (IS_MOVING(ELX, ELY) || IS_BLOCKED(ELX, ELY))
1056 element = MovingOrBlocked2Element(ELX, ELY);
1059 printf("HitElement (1): element == %d\n", element);
1063 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1064 printf("HitElement (%d): EXACT MATCH @ (%d, %d)\n", element, ELX, ELY);
1066 printf("HitElement (%d): FUZZY MATCH @ (%d, %d)\n", element, ELX, ELY);
1069 AddDamagedField(ELX, ELY);
1072 if (ELX != (LX + 5 * XS) / TILEX ||
1073 ELY != (LY + 5 * YS) / TILEY)
1083 /* this is more precise: check if laser would go through the center */
1084 if ((ELX * TILEX + 14 - LX) * YS != (ELY * TILEY + 14 - LY) * XS)
1086 /* skip the whole element before continuing the scan */
1092 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1094 if (LX/TILEX > ELX || LY/TILEY > ELY)
1096 /* skipping scan positions to the right and down skips one scan
1097 position too much, because this is only the top left scan position
1098 of totally four scan positions (plus one to the right, one to the
1099 bottom and one to the bottom right) */
1110 printf("HitElement (2): element == %d\n", element);
1113 if (LX + 5 * XS < 0 ||
1123 printf("HitElement (3): element == %d\n", element);
1126 if (IS_POLAR(element) &&
1127 ((element - EL_POLAR_START) % 2 ||
1128 (element - EL_POLAR_START) / 2 != laser.current_angle % 8))
1130 PlaySoundStereo(SND_KINK, ST(ELX));
1131 laser.num_damages--;
1136 if (IS_POLAR_CROSS(element) &&
1137 (element - EL_POLAR_CROSS_START) != laser.current_angle % 4)
1139 PlaySoundStereo(SND_KINK, ST(ELX));
1140 laser.num_damages--;
1145 if (!IS_BEAMER(element) &&
1146 !IS_FIBRE_OPTIC(element) &&
1147 !IS_GRID_WOOD(element) &&
1148 element != EL_FUEL_EMPTY)
1151 if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1152 printf("EXACT MATCH @ (%d, %d)\n", ELX, ELY);
1154 printf("FUZZY MATCH @ (%d, %d)\n", ELX, ELY);
1157 LX = ELX * TILEX + 14;
1158 LY = ELY * TILEY + 14;
1159 AddLaserEdge(LX, LY);
1162 if (IS_MIRROR(element) ||
1163 IS_MIRROR_FIXED(element) ||
1164 IS_POLAR(element) ||
1165 IS_POLAR_CROSS(element) ||
1166 IS_DF_MIRROR(element) ||
1167 IS_DF_MIRROR_AUTO(element) ||
1168 element == EL_PRISM ||
1169 element == EL_REFRACTOR)
1171 int current_angle = laser.current_angle;
1174 laser.num_damages--;
1175 AddDamagedField(ELX, ELY);
1176 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1179 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1181 if (IS_MIRROR(element) ||
1182 IS_MIRROR_FIXED(element) ||
1183 IS_DF_MIRROR(element) ||
1184 IS_DF_MIRROR_AUTO(element))
1185 laser.current_angle = get_mirrored_angle(laser.current_angle,
1186 get_element_angle(element));
1188 if (element == EL_PRISM || element == EL_REFRACTOR)
1189 laser.current_angle = RND(16);
1191 XS = 2 * Step[laser.current_angle].x;
1192 YS = 2 * Step[laser.current_angle].y;
1194 if (!IS_22_5_ANGLE(laser.current_angle)) /* 90° or 45° angle */
1199 LX += step_size * XS;
1200 LY += step_size * YS;
1203 /* draw sparkles on mirror */
1204 if ((IS_MIRROR(element) || IS_MIRROR_FIXED(element)) &&
1205 current_angle != laser.current_angle)
1207 MoveSprite(vp, &Pfeil[2], 4 + 16 * ELX, 5 + 16 * ELY + 1);
1211 if ((!IS_POLAR(element) && !IS_POLAR_CROSS(element)) &&
1212 current_angle != laser.current_angle)
1213 PlaySoundStereo(SND_LASER, ST(ELX));
1216 (get_opposite_angle(laser.current_angle) ==
1217 laser.damage[laser.num_damages - 1].angle ? TRUE : FALSE);
1219 return (laser.overloaded ? TRUE : FALSE);
1222 if (element == EL_FUEL_FULL)
1224 laser.stops_inside_element = TRUE;
1229 if (element == EL_BOMB || element == EL_MINE)
1231 PlaySoundStereo(SND_KINK, ST(ELX));
1233 if (element == EL_MINE)
1234 laser.overloaded = TRUE;
1237 if (element == EL_KETTLE ||
1238 element == EL_CELL ||
1239 element == EL_KEY ||
1240 element == EL_LIGHTBALL ||
1241 element == EL_PACMAN ||
1244 if (!IS_PACMAN(element))
1247 if (element == EL_PACMAN)
1250 if (element == EL_KETTLE || element == EL_CELL)
1253 if (game_mm.kettles_still_needed)
1254 DrawText(DX_KETTLES, DY_KETTLES,
1255 int2str(--game_mm.kettles_still_needed, 3), FONT_TEXT_2);
1259 if (game_mm.kettles_still_needed == 0)
1262 static int xy[4][2] =
1270 PlaySoundStereo(SND_KLING, ST(ELX));
1272 for(y=0; y<lev_fieldy; y++)
1274 for(x=0; x<lev_fieldx; x++)
1276 /* initiate opening animation of exit door */
1277 if (Feld[x][y] == EL_EXIT_CLOSED)
1278 Feld[x][y] = EL_EXIT_OPENING;
1280 /* remove field that blocks receiver */
1281 if (IS_RECEIVER(Feld[x][y]))
1283 int phase = Feld[x][y] - EL_RECEIVER_START;
1284 int blocking_x, blocking_y;
1286 blocking_x = x + xy[phase][0];
1287 blocking_y = y + xy[phase][1];
1289 if (IN_LEV_FIELD(blocking_x, blocking_y))
1291 Feld[blocking_x][blocking_y] = EL_EMPTY;
1292 DrawField_MM(blocking_x, blocking_y);
1298 DrawLaser(0, DL_LASER_ENABLED);
1301 else if (element == EL_KEY)
1303 else if (element == EL_LIGHTBALL)
1305 else if (IS_PACMAN(element))
1307 DeletePacMan(ELX, ELY);
1314 if (element == EL_LIGHTBULB_OFF || element == EL_LIGHTBULB_ON)
1316 PlaySoundStereo(SND_KINK, ST(ELX));
1318 DrawLaser(0, DL_LASER_ENABLED);
1320 if (Feld[ELX][ELY] == EL_LIGHTBULB_OFF)
1322 Feld[ELX][ELY] = EL_LIGHTBULB_ON;
1323 game_mm.lights_still_needed--;
1327 Feld[ELX][ELY] = EL_LIGHTBULB_OFF;
1328 game_mm.lights_still_needed++;
1331 DrawField_MM(ELX, ELY);
1332 DrawLaser(0, DL_LASER_ENABLED);
1337 laser.stops_inside_element = TRUE;
1343 printf("HitElement (4): element == %d\n", element);
1346 if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1347 laser.num_beamers < MAX_NUM_BEAMERS &&
1348 laser.beamer[BEAMER_NR(element)][1].num)
1350 int beamer_angle = get_element_angle(element);
1351 int beamer_nr = BEAMER_NR(element);
1355 printf("HitElement (BEAMER): element == %d\n", element);
1358 laser.num_damages--;
1360 if (IS_FIBRE_OPTIC(element) ||
1361 laser.current_angle == get_opposite_angle(beamer_angle))
1365 LX = ELX * TILEX + 14;
1366 LY = ELY * TILEY + 14;
1367 AddLaserEdge(LX, LY);
1368 AddDamagedField(ELX, ELY);
1369 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1372 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1374 pos = (ELX == laser.beamer[beamer_nr][0].x &&
1375 ELY == laser.beamer[beamer_nr][0].y ? 1 : 0);
1376 ELX = laser.beamer[beamer_nr][pos].x;
1377 ELY = laser.beamer[beamer_nr][pos].y;
1378 LX = ELX * TILEX + 14;
1379 LY = ELY * TILEY + 14;
1381 if (IS_BEAMER(element))
1383 laser.current_angle = get_element_angle(Feld[ELX][ELY]);
1384 XS = 2 * Step[laser.current_angle].x;
1385 YS = 2 * Step[laser.current_angle].y;
1388 laser.beamer_edge[laser.num_beamers] = laser.num_edges;
1389 AddLaserEdge(LX, LY);
1390 AddDamagedField(ELX, ELY);
1391 laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1394 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1396 if (laser.current_angle == (laser.current_angle >> 1) << 1)
1401 LX += step_size * XS;
1402 LY += step_size * YS;
1404 laser.num_beamers++;
1413 boolean HitOnlyAnEdge(int element, int hit_mask)
1415 /* check if the laser hit only the edge of an element and, if so, go on */
1418 printf("LX, LY, hit_mask == %d, %d, %d\n", LX, LY, hit_mask);
1421 if ((hit_mask == HIT_MASK_TOPLEFT ||
1422 hit_mask == HIT_MASK_TOPRIGHT ||
1423 hit_mask == HIT_MASK_BOTTOMLEFT ||
1424 hit_mask == HIT_MASK_BOTTOMRIGHT) &&
1425 laser.current_angle % 4) /* angle is not 90° */
1429 if (hit_mask == HIT_MASK_TOPLEFT)
1434 else if (hit_mask == HIT_MASK_TOPRIGHT)
1439 else if (hit_mask == HIT_MASK_BOTTOMLEFT)
1444 else /* (hit_mask == HIT_MASK_BOTTOMRIGHT) */
1450 AddDamagedField((LX + 2 * dx) / TILEX, (LY + 2 * dy) / TILEY);
1455 printf("[HitOnlyAnEdge() == TRUE]\n");
1462 printf("[HitOnlyAnEdge() == FALSE]\n");
1468 boolean HitPolarizer(int element, int hit_mask)
1470 if (HitOnlyAnEdge(element, hit_mask))
1473 if (IS_DF_GRID(element))
1475 int grid_angle = get_element_angle(element);
1478 printf("HitPolarizer: angle: grid == %d, laser == %d\n",
1479 grid_angle, laser.current_angle);
1482 AddLaserEdge(LX, LY);
1483 AddDamagedField(ELX, ELY);
1486 Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1488 if (laser.current_angle == grid_angle ||
1489 laser.current_angle == get_opposite_angle(grid_angle))
1494 if (!IS_22_5_ANGLE(laser.current_angle)) /* 90° or 45° angle */
1499 LX += step_size * XS;
1500 LY += step_size * YS;
1503 /* skip the whole element before continuing the scan */
1509 while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1511 if (LX/TILEX > ELX || LY/TILEY > ELY)
1513 /* skipping scan positions to the right and down skips one scan
1514 position too much, because this is only the top left scan position
1515 of totally four scan positions (plus one to the right, one to the
1516 bottom and one to the bottom right) */
1523 AddLaserEdge(LX, LY);
1529 printf("HitPolarizer: LX, LY == %d, %d [%d, %d] [%d, %d]\n",
1531 LX / TILEX, LY / TILEY,
1532 LX % TILEX, LY % TILEY);
1537 else if (IS_GRID_STEEL_FIXED(element) || IS_GRID_STEEL_AUTO(element))
1538 return HitReflectingWalls(element, hit_mask);
1540 return HitAbsorbingWalls(element, hit_mask);
1542 else if (IS_GRID_STEEL(element))
1543 return HitReflectingWalls(element, hit_mask);
1544 else /* IS_GRID_WOOD */
1545 return HitAbsorbingWalls(element, hit_mask);
1550 boolean HitBlock(int element, int hit_mask)
1552 boolean check = FALSE;
1554 if ((element == EL_GATE_STONE || element == EL_GATE_WOOD) &&
1555 game_mm.num_keys == 0)
1558 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1561 int ex = ELX * TILEX + 14;
1562 int ey = ELY * TILEY + 14;
1571 if ((x == ex || x == ex + 1) && (y == ey || y == ey + 1))
1576 if (check && (element == EL_BLOCK_WOOD || element == EL_GATE_WOOD))
1577 return HitAbsorbingWalls(element, hit_mask);
1581 AddLaserEdge(LX - XS, LY - YS);
1582 AddDamagedField(ELX, ELY);
1585 Box[ELX][ELY] = laser.num_edges;
1587 return HitReflectingWalls(element, hit_mask);
1590 if (element == EL_GATE_STONE || element == EL_GATE_WOOD)
1592 int xs = XS / 2, ys = YS / 2;
1593 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1594 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1596 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1597 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1599 laser.overloaded = (element == EL_GATE_STONE);
1603 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1604 (hit_mask == HIT_MASK_TOP ||
1605 hit_mask == HIT_MASK_LEFT ||
1606 hit_mask == HIT_MASK_RIGHT ||
1607 hit_mask == HIT_MASK_BOTTOM))
1608 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1609 hit_mask == HIT_MASK_BOTTOM),
1610 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1611 hit_mask == HIT_MASK_RIGHT));
1612 AddLaserEdge(LX, LY);
1617 if (element == EL_GATE_STONE && Box[ELX][ELY])
1619 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
1631 if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1633 int xs = XS / 2, ys = YS / 2;
1634 int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1635 int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1637 if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1638 (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1640 laser.overloaded = (element == EL_BLOCK_STONE);
1645 if (ABS(xs) == 1 && ABS(ys) == 1 &&
1646 (hit_mask == HIT_MASK_TOP ||
1647 hit_mask == HIT_MASK_LEFT ||
1648 hit_mask == HIT_MASK_RIGHT ||
1649 hit_mask == HIT_MASK_BOTTOM))
1650 AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1651 hit_mask == HIT_MASK_BOTTOM),
1652 ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1653 hit_mask == HIT_MASK_RIGHT));
1654 AddDamagedField(ELX, ELY);
1656 LX = ELX * TILEX + 14;
1657 LY = ELY * TILEY + 14;
1658 AddLaserEdge(LX, LY);
1660 laser.stops_inside_element = TRUE;
1668 boolean HitLaserSource(int element, int hit_mask)
1670 if (HitOnlyAnEdge(element, hit_mask))
1673 PlaySoundStereo(SND_AUTSCH, ST(ELX));
1674 laser.overloaded = TRUE;
1679 boolean HitLaserDestination(int element, int hit_mask)
1681 if (HitOnlyAnEdge(element, hit_mask))
1684 if (element != EL_EXIT_OPEN &&
1685 !(IS_RECEIVER(element) &&
1686 game_mm.kettles_still_needed == 0 &&
1687 laser.current_angle == get_opposite_angle(get_element_angle(element))))
1689 PlaySoundStereo(SND_HOLZ, ST(ELX));
1693 if (IS_RECEIVER(element) ||
1694 (IS_22_5_ANGLE(laser.current_angle) &&
1695 (ELX != (LX + 6 * XS) / TILEX ||
1696 ELY != (LY + 6 * YS) / TILEY ||
1705 LX = ELX * TILEX + 14;
1706 LY = ELY * TILEY + 14;
1708 laser.stops_inside_element = TRUE;
1711 AddLaserEdge(LX, LY);
1712 AddDamagedField(ELX, ELY);
1714 if (game_mm.lights_still_needed == 0)
1715 game_mm.level_solved = TRUE;
1720 boolean HitReflectingWalls(int element, int hit_mask)
1722 /* check if laser hits side of a wall with an angle that is not 90° */
1723 if (!IS_90_ANGLE(laser.current_angle) && (hit_mask == HIT_MASK_TOP ||
1724 hit_mask == HIT_MASK_LEFT ||
1725 hit_mask == HIT_MASK_RIGHT ||
1726 hit_mask == HIT_MASK_BOTTOM))
1728 PlaySoundStereo(SND_HUI, ST(ELX));
1731 if (!IS_DF_GRID(element))
1732 AddLaserEdge(LX, LY);
1734 /* check if laser hits wall with an angle of 45° */
1735 if (!IS_22_5_ANGLE(laser.current_angle))
1737 if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1740 laser.current_angle = get_mirrored_angle(laser.current_angle,
1743 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1746 laser.current_angle = get_mirrored_angle(laser.current_angle,
1750 AddLaserEdge(LX, LY);
1751 XS = 2 * Step[laser.current_angle].x;
1752 YS = 2 * Step[laser.current_angle].y;
1756 else if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1758 laser.current_angle = get_mirrored_angle(laser.current_angle,
1763 if (!IS_DF_GRID(element))
1764 AddLaserEdge(LX, LY);
1769 if (!IS_DF_GRID(element))
1770 AddLaserEdge(LX, LY + YS / 2);
1773 if (!IS_DF_GRID(element))
1774 AddLaserEdge(LX, LY);
1777 YS = 2 * Step[laser.current_angle].y;
1781 else /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1783 laser.current_angle = get_mirrored_angle(laser.current_angle,
1788 if (!IS_DF_GRID(element))
1789 AddLaserEdge(LX, LY);
1794 if (!IS_DF_GRID(element))
1795 AddLaserEdge(LX + XS / 2, LY);
1798 if (!IS_DF_GRID(element))
1799 AddLaserEdge(LX, LY);
1802 XS = 2 * Step[laser.current_angle].x;
1808 /* reflection at the edge of reflecting DF style wall */
1809 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1811 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1812 hit_mask == HIT_MASK_TOPRIGHT) ||
1813 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1814 hit_mask == HIT_MASK_TOPLEFT) ||
1815 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1816 hit_mask == HIT_MASK_BOTTOMLEFT) ||
1817 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1818 hit_mask == HIT_MASK_BOTTOMRIGHT))
1821 (hit_mask == HIT_MASK_TOPRIGHT || hit_mask == HIT_MASK_BOTTOMLEFT ?
1822 ANG_MIRROR_135 : ANG_MIRROR_45);
1824 PlaySoundStereo(SND_HUI, ST(ELX));
1825 AddDamagedField(ELX, ELY);
1826 AddLaserEdge(LX, LY);
1828 laser.current_angle = get_mirrored_angle(laser.current_angle,
1835 AddLaserEdge(LX, LY);
1841 /* reflection inside an edge of reflecting DF style wall */
1842 if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1844 if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1845 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT)) ||
1846 ((laser.current_angle == 5 || laser.current_angle == 7) &&
1847 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMRIGHT)) ||
1848 ((laser.current_angle == 9 || laser.current_angle == 11) &&
1849 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT)) ||
1850 ((laser.current_angle == 13 || laser.current_angle == 15) &&
1851 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPLEFT)))
1854 (hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT) ||
1855 hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT) ?
1856 ANG_MIRROR_135 : ANG_MIRROR_45);
1858 PlaySoundStereo(SND_HUI, ST(ELX));
1860 AddDamagedField(ELX, ELY);
1862 AddLaserEdge(LX - XS, LY - YS);
1863 AddLaserEdge(LX - XS + (ABS(XS) == 4 ? XS/2 : 0),
1864 LY - YS + (ABS(YS) == 4 ? YS/2 : 0));
1866 laser.current_angle = get_mirrored_angle(laser.current_angle,
1873 AddLaserEdge(LX, LY);
1879 /* check if laser hits DF style wall with an angle of 90° */
1880 if (IS_DF_WALL(element) && IS_90_ANGLE(laser.current_angle))
1882 if ((IS_HORIZ_ANGLE(laser.current_angle) &&
1883 (!(hit_mask & HIT_MASK_TOP) || !(hit_mask & HIT_MASK_BOTTOM))) ||
1884 (IS_VERT_ANGLE(laser.current_angle) &&
1885 (!(hit_mask & HIT_MASK_LEFT) || !(hit_mask & HIT_MASK_RIGHT))))
1887 static int last_LX = 0, last_LY = 0, last_hit_mask = 0;
1889 /* laser at last step touched nothing or the same side of the wall */
1890 if (LX != last_LX || LY != last_LY || hit_mask == last_hit_mask)
1892 AddDamagedField(ELX, ELY);
1898 last_hit_mask = hit_mask;
1905 if (!HitOnlyAnEdge(element, hit_mask))
1907 laser.overloaded = TRUE;
1914 boolean HitAbsorbingWalls(int element, int hit_mask)
1916 if (HitOnlyAnEdge(element, hit_mask))
1920 (hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT))
1922 AddLaserEdge(LX - XS, LY - YS);
1928 (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM))
1930 AddLaserEdge(LX - XS, LY - YS);
1935 if (IS_WALL_WOOD(element) ||
1936 IS_DF_WALL_WOOD(element) ||
1937 IS_GRID_WOOD(element) ||
1938 IS_GRID_WOOD_FIXED(element) ||
1939 IS_GRID_WOOD_AUTO(element) ||
1940 element == EL_FUSE_ON ||
1941 element == EL_BLOCK_WOOD ||
1942 element == EL_GATE_WOOD)
1944 PlaySoundStereo(SND_HOLZ, ST(ELX));
1948 if (IS_WALL_ICE(element))
1952 mask = (LX + XS) / MINI_TILEX - ELX * 2 + 1; /* Quadrant (horizontal) */
1953 mask <<= (((LY + YS) / MINI_TILEY - ELY * 2) > 0) * 2; /* || (vertical) */
1955 /* check if laser hits wall with an angle of 90° */
1956 if (IS_90_ANGLE(laser.current_angle))
1957 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1959 if (mask == 1 || mask == 2 || mask == 4 || mask == 8)
1965 if (mask == (1 << i) && (XS > 0) == (i % 2) && (YS > 0) == (i / 2))
1966 mask = 15 - (8 >> i);
1967 else if (ABS(XS) == 4 &&
1969 (XS > 0) == (i % 2) &&
1970 (YS < 0) == (i / 2))
1971 mask = 3 + (i / 2) * 9;
1972 else if (ABS(YS) == 4 &&
1974 (XS < 0) == (i % 2) &&
1975 (YS > 0) == (i / 2))
1976 mask = 5 + (i % 2) * 5;
1980 laser.wall_mask = mask;
1982 else if (IS_WALL_AMOEBA(element))
1984 int elx = (LX - 2 * XS) / TILEX;
1985 int ely = (LY - 2 * YS) / TILEY;
1986 int element2 = Feld[elx][ely];
1989 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element2))
1991 laser.dest_element = EL_EMPTY;
1998 mask = (LX - 2 * XS) / 16 - ELX * 2 + 1;
1999 mask <<= ((LY - 2 * YS) / 16 - ELY * 2) * 2;
2001 if (IS_90_ANGLE(laser.current_angle))
2002 mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
2004 laser.dest_element = element2 | EL_WALL_AMOEBA;
2006 laser.wall_mask = mask;
2012 void OpenExit(int x, int y)
2016 if (!MovDelay[x][y]) /* next animation frame */
2017 MovDelay[x][y] = 4 * delay;
2019 if (MovDelay[x][y]) /* wait some time before next frame */
2024 phase = MovDelay[x][y] / delay;
2025 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2026 DrawGraphic_MM(x, y, EL_EXIT_OPEN - phase);
2028 if (!MovDelay[x][y])
2030 Feld[x][y] = EL_EXIT_OPEN;
2036 void OpenSurpriseBall(int x, int y)
2040 if (!MovDelay[x][y]) /* next animation frame */
2041 MovDelay[x][y] = 50 * delay;
2043 if (MovDelay[x][y]) /* wait some time before next frame */
2051 phase = MovDelay[x][y] / delay;
2053 if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2056 int graphic = el2gfx(Store[x][y]);
2058 int dx = RND(26), dy = RND(26);
2060 getGraphicSource(graphic, 0, &bitmap, &gx, &gy);
2061 BlitBitmap(bitmap, drawto, gx + dx, gy + dy, 6, 6,
2062 SX + x * TILEX + dx, SY + y * TILEY + dy);
2063 MarkTileDirty(x, y);
2066 if (!MovDelay[x][y])
2068 Feld[x][y] = Store[x][y];
2077 void MeltIce(int x, int y)
2082 if (!MovDelay[x][y]) /* next animation frame */
2083 MovDelay[x][y] = frames * delay;
2085 if (MovDelay[x][y]) /* wait some time before next frame */
2088 int wall_mask = Store2[x][y];
2089 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_ICE;
2092 phase = frames - MovDelay[x][y] / delay - 1;
2094 if (!MovDelay[x][y])
2098 Feld[x][y] = real_element & (wall_mask ^ 0xFF);
2099 Store[x][y] = Store2[x][y] = 0;
2101 DrawWalls_MM(x, y, Feld[x][y]);
2103 if (Feld[x][y] == EL_WALL_ICE)
2104 Feld[x][y] = EL_EMPTY;
2106 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
2107 if (laser.damage[i].is_mirror)
2111 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
2113 DrawLaser(0, DL_LASER_DISABLED);
2117 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2119 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2121 laser.redraw = TRUE;
2126 void GrowAmoeba(int x, int y)
2131 if (!MovDelay[x][y]) /* next animation frame */
2132 MovDelay[x][y] = frames * delay;
2134 if (MovDelay[x][y]) /* wait some time before next frame */
2137 int wall_mask = Store2[x][y];
2138 int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_AMOEBA;
2141 phase = MovDelay[x][y] / delay;
2143 if (!MovDelay[x][y])
2145 Feld[x][y] = real_element;
2146 Store[x][y] = Store2[x][y] = 0;
2148 DrawWalls_MM(x, y, Feld[x][y]);
2149 DrawLaser(0, DL_LASER_ENABLED);
2151 else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2152 DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2156 void Explode(int x, int y, int phase, int mode)
2158 int num_phase = 9, delay = 2;
2159 int last_phase = num_phase * delay;
2160 int half_phase = (num_phase / 2) * delay;
2162 int first_phase_after_start = EX_PHASE_START + 1;
2165 laser.redraw = TRUE;
2167 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
2169 int center_element = Feld[x][y];
2171 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2173 /* put moving element to center field (and let it explode there) */
2174 center_element = MovingOrBlocked2Element(x, y);
2175 RemoveMovingField(x, y);
2176 Feld[x][y] = center_element;
2179 if (center_element == EL_BOMB || IS_MCDUFFIN(center_element))
2180 Store[x][y] = center_element;
2182 Store[x][y] = EL_EMPTY;
2183 Store2[x][y] = mode;
2184 Feld[x][y] = EL_EXPLODING_OPAQUE;
2185 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2191 Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
2193 if (phase == half_phase)
2195 Feld[x][y] = EL_EXPLODING_TRANSP;
2197 if (x == ELX && y == ELY)
2201 if (phase == last_phase)
2207 if (Store[x][y] == EL_BOMB)
2209 laser.num_damages--;
2210 DrawLaser(0, DL_LASER_DISABLED);
2211 laser.num_edges = 0;
2213 Bang(laser.start_edge.x, laser.start_edge.y);
2214 Store[x][y] = EL_EMPTY;
2216 else if (IS_MCDUFFIN(Store[x][y]))
2218 game_mm.game_over = TRUE;
2219 game_mm.game_over_cause = GAME_OVER_BOMB;
2220 Store[x][y] = EL_EMPTY;
2224 element = Feld[x][y] = Store[x][y];
2226 Store[x][y] = Store2[x][y] = 0;
2227 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2228 InitField(x, y, FALSE);
2231 else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2233 int graphic = GFX_EXPLOSION_START;
2234 int graphic_phase = (phase / delay - 1);
2236 if (Store2[x][y] == EX_KETTLE)
2238 if (graphic_phase < 3)
2239 graphic = GFX_EXPLOSION_KETTLE;
2240 else if (graphic_phase < 5)
2242 graphic = GFX_EXPLOSION_LAST;
2243 graphic_phase -= graphic_phase;
2247 graphic = GFX_EMPTY;
2251 else if (Store2[x][y] == EX_SHORT)
2253 if (graphic_phase < 4)
2254 graphic = GFX_EXPLOSION_SHORT;
2257 graphic = GFX_EMPTY;
2262 DrawGraphic_MM(x, y, graphic + graphic_phase);
2266 void Bang(int x, int y)
2268 int element = Feld[x][y];
2269 int mode = EX_NORMAL;
2272 DrawLaser(0, DL_LASER_ENABLED);
2291 if (IS_PACMAN(element))
2292 PlaySoundStereo(SND_QUIEK, ST(x));
2293 else if (element == EL_BOMB || IS_MCDUFFIN(element))
2294 PlaySoundStereo(SND_ROAAAR, ST(x));
2295 else if (element == EL_KEY)
2296 PlaySoundStereo(SND_KLING, ST(x));
2298 PlaySoundStereo((mode == EX_SHORT ? SND_WHOOSH : SND_KABUMM), ST(x));
2300 Explode(x, y, EX_PHASE_START, mode);
2303 void TurnRound(int x, int y)
2315 { 0, 0 }, { 0, 0 }, { 0, 0 },
2320 int left, right, back;
2324 { MV_DOWN, MV_UP, MV_RIGHT },
2325 { MV_UP, MV_DOWN, MV_LEFT },
2327 { MV_LEFT, MV_RIGHT, MV_DOWN },
2328 { 0,0,0 }, { 0,0,0 }, { 0,0,0 },
2329 { MV_RIGHT, MV_LEFT, MV_UP }
2332 int element = Feld[x][y];
2333 int old_move_dir = MovDir[x][y];
2335 int left_dir = turn[old_move_dir].left;
2337 int right_dir = turn[old_move_dir].right;
2338 int back_dir = turn[old_move_dir].back;
2341 int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
2343 int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
2346 int left_x = x+left_dx, left_y = y+left_dy;
2348 int right_x = x+right_dx, right_y = y+right_dy;
2350 if (element == EL_PACMAN)
2353 boolean can_turn_left = FALSE;
2355 boolean can_turn_right = FALSE;
2358 if (IN_LEV_FIELD(left_x, left_y) &&
2359 IS_EATABLE4PACMAN(Feld[left_x][left_y]))
2360 can_turn_left = TRUE;
2362 if (IN_LEV_FIELD(right_x, right_y) &&
2363 IS_EATABLE4PACMAN(Feld[right_x][right_y]))
2364 can_turn_right = TRUE;
2367 MovDir[x][y] = right_dir;
2369 MovDir[x][y] = back_dir;
2375 void StartMoving(int x, int y)
2377 int element = Feld[x][y];
2382 if (CAN_MOVE(element))
2386 if (MovDelay[x][y]) /* wait some time before next movement */
2394 /* now make next step */
2396 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
2398 if (element == EL_PACMAN &&
2399 IN_LEV_FIELD(newx, newy) && IS_EATABLE4PACMAN(Feld[newx][newy]) &&
2400 !ObjHit(newx, newy, HIT_POS_CENTER))
2402 Store[newx][newy] = Feld[newx][newy];
2403 Feld[newx][newy] = EL_EMPTY;
2404 DrawField_MM(newx, newy);
2406 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy) ||
2407 ObjHit(newx, newy, HIT_POS_CENTER))
2409 /* object was running against a wall */
2416 InitMovingField(x, y, MovDir[x][y]);
2420 ContinueMoving(x, y);
2423 void ContinueMoving(int x, int y)
2425 int element = Feld[x][y];
2426 int direction = MovDir[x][y];
2427 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2428 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2429 int horiz_move = (dx!=0);
2430 int newx = x + dx, newy = y + dy;
2431 int step = (horiz_move ? dx : dy) * TILEX / 8;
2433 MovPos[x][y] += step;
2435 if (ABS(MovPos[x][y]) >= TILEX) /* object reached its destination */
2437 Feld[x][y] = EL_EMPTY;
2438 Feld[newx][newy] = element;
2440 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2441 MovDelay[newx][newy] = 0;
2443 if (!CAN_MOVE(element))
2444 MovDir[newx][newy] = 0;
2447 DrawField_MM(newx, newy);
2449 Stop[newx][newy] = TRUE;
2451 if (element == EL_PACMAN)
2453 if (Store[newx][newy] == EL_BOMB)
2456 if (IS_WALL_AMOEBA(Store[newx][newy]) &&
2457 (LX + 2 * XS) / TILEX == newx &&
2458 (LY + 2 * YS) / TILEY == newy)
2465 else /* still moving on */
2468 laser.redraw = TRUE;
2471 void ClickElement(int mx, int my, int button)
2473 static unsigned int click_delay = 0;
2474 static int click_delay_value = CLICK_DELAY_SHORT;
2475 static boolean new_button = TRUE;
2477 int x = (mx - SX) / TILEX, y = (my - SY) / TILEY;
2479 if (button == MB_RELEASED)
2482 click_delay_value = CLICK_DELAY_SHORT;
2484 /* release eventually hold auto-rotating mirror */
2485 RotateMirror(x, y, MB_RELEASED);
2490 if (!DelayReached(&click_delay, click_delay_value) && !new_button)
2493 if (button == MB_MIDDLEBUTTON) /* middle button has no function */
2496 if (!IN_PIX_FIELD(mx - SX, my - SY))
2499 if (Feld[x][y] == EL_EMPTY)
2502 element = Feld[x][y];
2504 if (IS_MIRROR(element) ||
2505 IS_BEAMER(element) ||
2506 IS_POLAR(element) ||
2507 IS_POLAR_CROSS(element) ||
2508 IS_DF_MIRROR(element) ||
2509 IS_DF_MIRROR_AUTO(element))
2511 RotateMirror(x, y, button);
2513 else if (IS_MCDUFFIN(element))
2515 if (!laser.fuse_off)
2517 DrawLaser(0, DL_LASER_DISABLED);
2523 element = get_rotated_element(element, BUTTON_ROTATION(button));
2524 laser.start_angle = get_element_angle(element);
2528 Feld[x][y] = element;
2533 if (!laser.fuse_off)
2536 else if (element == EL_FUSE_ON && laser.fuse_off)
2538 if (x != laser.fuse_x || y != laser.fuse_y)
2541 laser.fuse_off = FALSE;
2542 laser.fuse_x = laser.fuse_y = -1;
2544 DrawGraphic_MM(x, y, GFX_FUSE_ON);
2547 else if (element == EL_FUSE_ON && !laser.fuse_off && new_button)
2549 laser.fuse_off = TRUE;
2552 laser.overloaded = FALSE;
2554 DrawLaser(0, DL_LASER_DISABLED);
2555 DrawGraphic_MM(x, y, GFX_FUSE_OFF);
2557 else if (element == EL_LIGHTBALL)
2561 DrawLaser(0, DL_LASER_ENABLED);
2564 click_delay_value = (new_button ? CLICK_DELAY_LONG : CLICK_DELAY_SHORT);
2568 void RotateMirror(int x, int y, int button)
2570 static int hold_x = -1, hold_y = -1;
2572 if (button == MB_RELEASED)
2574 /* release eventually hold auto-rotating mirror */
2581 if (IS_MIRROR(Feld[x][y]) ||
2582 IS_POLAR_CROSS(Feld[x][y]) ||
2583 IS_POLAR(Feld[x][y]) ||
2584 IS_BEAMER(Feld[x][y]) ||
2585 IS_DF_MIRROR(Feld[x][y]) ||
2586 IS_GRID_STEEL_AUTO(Feld[x][y]) ||
2587 IS_GRID_WOOD_AUTO(Feld[x][y]))
2589 Feld[x][y] = get_rotated_element(Feld[x][y], BUTTON_ROTATION(button));
2591 else if (IS_DF_MIRROR_AUTO(Feld[x][y]))
2593 if (button == MB_LEFTBUTTON)
2595 /* left mouse button only for manual adjustment, no auto-rotating;
2596 freeze mirror for until mouse button released */
2600 else if (button == MB_RIGHTBUTTON && (hold_x != x || hold_y != y))
2601 Feld[x][y] = get_rotated_element(Feld[x][y], ROTATE_RIGHT);
2604 if (IS_GRID_STEEL_AUTO(Feld[x][y]) || IS_GRID_WOOD_AUTO(Feld[x][y]))
2606 int edge = Hit[x][y];
2612 DrawLaser(edge - 1, DL_LASER_DISABLED);
2616 else if (ObjHit(x, y, HIT_POS_CENTER))
2618 int edge = Hit[x][y];
2622 Error(ERR_WARN, "RotateMirror: inconsistent field Hit[][]!\n");
2626 DrawLaser(edge - 1, DL_LASER_DISABLED);
2633 if (ObjHit(x, y, HIT_POS_EDGE | HIT_POS_BETWEEN))
2638 if ((IS_BEAMER(Feld[x][y]) ||
2639 IS_POLAR(Feld[x][y]) ||
2640 IS_POLAR_CROSS(Feld[x][y])) && x == ELX && y == ELY)
2644 if (IS_BEAMER(Feld[x][y]))
2647 printf("TEST (%d, %d) [%d] [%d]\n",
2649 laser.beamer_edge, laser.beamer[1].num);
2659 DrawLaser(0, DL_LASER_ENABLED);
2663 void AutoRotateMirrors()
2665 static unsigned int rotate_delay = 0;
2668 if (!DelayReached(&rotate_delay, AUTO_ROTATE_DELAY))
2671 for (x=0; x<lev_fieldx; x++)
2673 for (y=0; y<lev_fieldy; y++)
2675 int element = Feld[x][y];
2677 if (IS_DF_MIRROR_AUTO(element) ||
2678 IS_GRID_WOOD_AUTO(element) ||
2679 IS_GRID_STEEL_AUTO(element) ||
2680 element == EL_REFRACTOR)
2681 RotateMirror(x, y, MB_RIGHTBUTTON);
2686 boolean ObjHit(int obx, int oby, int bits)
2693 if (bits & HIT_POS_CENTER)
2695 if (ReadPixel(drawto, SX + obx + 15, SY + oby + 15) == pen_ray)
2699 if (bits & HIT_POS_EDGE)
2702 if (ReadPixel(drawto,
2703 SX + obx + 31 * (i % 2),
2704 SY + oby + 31 * (i / 2)) == pen_ray)
2708 if (bits & HIT_POS_BETWEEN)
2711 if (ReadPixel(drawto,
2712 SX + 4 + obx + 22 * (i % 2),
2713 SY + 4 + oby + 22 * (i / 2)) == pen_ray)
2720 void DeletePacMan(int px, int py)
2726 if (game_mm.num_pacman <= 1)
2728 game_mm.num_pacman = 0;
2732 for(i=0; i<game_mm.num_pacman; i++)
2733 if (game_mm.pacman[i].x == px && game_mm.pacman[i].y == py)
2736 game_mm.num_pacman--;
2738 for(j=i; j<game_mm.num_pacman; j++)
2740 game_mm.pacman[j].x = game_mm.pacman[j + 1].x;
2741 game_mm.pacman[j].y = game_mm.pacman[j + 1].y;
2742 game_mm.pacman[j].dir = game_mm.pacman[j + 1].dir;
2746 void ColorCycling(void)
2748 static int CC, Cc = 0;
2750 static int color, old = 0xF00, new = 0x010, mult = 1;
2751 static unsigned short red, green, blue;
2753 if (color_status == STATIC_COLORS)
2758 if (CC < Cc || CC > Cc + 50)
2762 color = old + new * mult;
2768 if (ABS(mult) == 16)
2777 red = 0x0e00 * ((color & 0xF00) >> 8);
2778 green = 0x0e00 * ((color & 0x0F0) >> 4);
2779 blue = 0x0e00 * ((color & 0x00F));
2780 SetRGB(pen_magicolor[0], red, green, blue);
2782 red = 0x1111 * ((color & 0xF00) >> 8);
2783 green = 0x1111 * ((color & 0x0F0) >> 4);
2784 blue = 0x1111 * ((color & 0x00F));
2785 SetRGB(pen_magicolor[1], red, green, blue);
2791 static unsigned int action_delay = 0;
2792 static unsigned int pacman_delay = 0;
2793 static unsigned int energy_delay = 0;
2794 static unsigned int overload_delay = 0;
2796 unsigned short color_scale = 0xFFFF / 15;
2804 WaitUntilDelayReached(&action_delay, GameFrameDelay);
2806 if (!DelayReached(&action_delay, GameFrameDelay))
2808 if (!PendingEvent()) /* delay only if no pending events */
2814 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
2817 for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
2819 element = Feld[x][y];
2821 if (!IS_MOVING(x, y) && CAN_MOVE(element))
2823 else if (IS_MOVING(x, y))
2824 ContinueMoving(x, y);
2825 else if (IS_EXPLODING(element))
2826 Explode(x, y, Frame[x][y], EX_NORMAL);
2827 else if (element == EL_EXIT_OPENING)
2829 else if (element == EL_GRAY_BALL_OPENING)
2830 OpenSurpriseBall(x, y);
2831 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_ICE)
2833 else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_AMOEBA)
2837 AutoRotateMirrors();
2840 /* !!! CHANGE THIS: REDRAW ONLY WHEN NEEDED !!! */
2842 /* redraw after Explode() ... */
2844 DrawLaser(0, DL_LASER_ENABLED);
2845 laser.redraw = FALSE;
2850 if (game_mm.num_pacman && DelayReached(&pacman_delay, 250))
2854 if (laser.num_damages > MAX_LASER_LEN && !laser.fuse_off)
2856 DrawLaser(0, DL_LASER_DISABLED);
2861 if (DelayReached(&energy_delay, 4000))
2863 game_mm.energy_left--;
2864 if (game_mm.energy_left >= 0)
2866 BlitBitmap(pix[PIX_DOOR], drawto,
2867 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
2868 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
2869 DX_ENERGY, DY_ENERGY);
2870 redraw_mask |= REDRAW_DOOR_1;
2872 else if (setup.time_limit)
2876 for(i=15; i>=0; i--)
2879 SetRGB(pen_ray, 0x0000, 0x0000, i * color_scale);
2881 pen_ray = GetPixelFromRGB(window,
2882 native_mm_level.laser_red * 0x11 * i,
2883 native_mm_level.laser_green * 0x11 * i,
2884 native_mm_level.laser_blue * 0x11 * i);
2885 DrawLaser(0, DL_LASER_ENABLED);
2890 StopSound(SND_WARNTON);
2893 DrawLaser(0, DL_LASER_DISABLED);
2894 game_mm.game_over = TRUE;
2895 game_mm.game_over_cause = GAME_OVER_NO_ENERGY;
2898 if (Request("Out of magic energy ! Play it again ?",
2899 REQ_ASK | REQ_STAY_CLOSED))
2905 game_status = MAINMENU;
2914 element = laser.dest_element;
2917 if (element != Feld[ELX][ELY])
2919 printf("element == %d, Feld[ELX][ELY] == %d\n",
2920 element, Feld[ELX][ELY]);
2924 if (!laser.overloaded && laser.overload_value == 0 &&
2925 element != EL_BOMB &&
2926 element != EL_MINE &&
2927 element != EL_BALL_GRAY &&
2928 element != EL_BLOCK_STONE &&
2929 element != EL_BLOCK_WOOD &&
2930 element != EL_FUSE_ON &&
2931 element != EL_FUEL_FULL &&
2932 !IS_WALL_ICE(element) &&
2933 !IS_WALL_AMOEBA(element))
2936 if (((laser.overloaded && laser.overload_value < MAX_LASER_OVERLOAD) ||
2937 (!laser.overloaded && laser.overload_value > 0)) &&
2938 DelayReached(&overload_delay, 60 + !laser.overloaded * 120))
2940 if (laser.overloaded)
2941 laser.overload_value++;
2943 laser.overload_value--;
2945 if (game_mm.cheat_no_overload)
2947 laser.overloaded = FALSE;
2948 laser.overload_value = 0;
2951 if (laser.overload_value < MAX_LASER_OVERLOAD - 8)
2953 int color_up = 0xFF * laser.overload_value / MAX_LASER_OVERLOAD;
2954 int color_down = 0xFF - color_up;
2957 SetRGB(pen_ray, (laser.overload_value / 6) * color_scale, 0x0000,
2958 (15 - (laser.overload_value / 6)) * color_scale);
2960 pen_ray = GetPixelFromRGB(window,
2961 (native_mm_level.laser_red ? 0xFF : color_up),
2962 (native_mm_level.laser_green ? color_down : 0x00),
2963 (native_mm_level.laser_blue ? color_down : 0x00));
2964 DrawLaser(0, DL_LASER_ENABLED);
2968 if (laser.overloaded)
2970 if (setup.sound_loops)
2971 PlaySoundExt(SND_WARNTON, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT, SND_CTRL_PLAY_LOOP);
2973 PlaySoundStereo(SND_WARNTON, SOUND_MAX_RIGHT);
2976 if (!laser.overloaded)
2977 StopSound(SND_WARNTON);
2979 if (laser.overloaded)
2981 BlitBitmap(pix[PIX_DOOR], drawto,
2982 DOOR_GFX_PAGEX4 + XX_OVERLOAD,
2983 DOOR_GFX_PAGEY1 + YY_OVERLOAD + OVERLOAD_YSIZE
2984 - laser.overload_value,
2985 OVERLOAD_XSIZE, laser.overload_value,
2986 DX_OVERLOAD, DY_OVERLOAD + OVERLOAD_YSIZE
2987 - laser.overload_value);
2988 redraw_mask |= REDRAW_DOOR_1;
2992 BlitBitmap(pix[PIX_DOOR], drawto,
2993 DOOR_GFX_PAGEX5 + XX_OVERLOAD, DOOR_GFX_PAGEY1 + YY_OVERLOAD,
2994 OVERLOAD_XSIZE, OVERLOAD_YSIZE - laser.overload_value,
2995 DX_OVERLOAD, DY_OVERLOAD);
2996 redraw_mask |= REDRAW_DOOR_1;
2999 if (laser.overload_value == MAX_LASER_OVERLOAD)
3003 for(i=15; i>=0; i--)
3006 SetRGB(pen_ray, i * color_scale, 0x0000, 0x0000);
3009 pen_ray = GetPixelFromRGB(window, 0x11 * i, 0x00, 0x00);
3010 DrawLaser(0, DL_LASER_ENABLED);
3015 DrawLaser(0, DL_LASER_DISABLED);
3016 game_mm.game_over = TRUE;
3017 game_mm.game_over_cause = GAME_OVER_OVERLOADED;
3020 if (Request("Magic spell hit Mc Duffin ! Play it again ?",
3021 REQ_ASK | REQ_STAY_CLOSED))
3027 game_status = MAINMENU;
3041 if (element == EL_BOMB && CT > 1500)
3043 if (game_mm.cheat_no_explosion)
3047 laser.num_damages--;
3048 DrawLaser(0, DL_LASER_DISABLED);
3049 laser.num_edges = 0;
3054 laser.dest_element = EL_EXPLODING_OPAQUE;
3058 laser.num_damages--;
3059 DrawLaser(0, DL_LASER_DISABLED);
3061 laser.num_edges = 0;
3062 Bang(laser.start_edge.x, laser.start_edge.y);
3064 if (Request("Bomb killed Mc Duffin ! Play it again ?",
3065 REQ_ASK | REQ_STAY_CLOSED))
3071 game_status = MAINMENU;
3079 if (element == EL_FUSE_ON && CT > 500)
3081 laser.fuse_off = TRUE;
3084 DrawLaser(0, DL_LASER_DISABLED);
3085 DrawGraphic_MM(ELX, ELY, GFX_FUSE_OFF);
3088 if (element == EL_BALL_GRAY && CT > 1500)
3090 static int new_elements[] =
3093 EL_MIRROR_FIXED_START,
3095 EL_POLAR_CROSS_START,
3101 int num_new_elements = sizeof(new_elements) / sizeof(int);
3102 int new_element = new_elements[RND(num_new_elements)];
3104 Store[ELX][ELY] = new_element + RND(get_num_elements(new_element));
3105 Feld[ELX][ELY] = EL_GRAY_BALL_OPENING;
3107 /* !!! CHECK AGAIN: Laser on Polarizer !!! */
3118 element = EL_MIRROR_START + RND(16);
3124 element = (rnd == 0 ? EL_KETTLE : rnd == 1 ? EL_BOMB : EL_PRISM);
3131 element = (rnd == 0 ? EL_FUSE_ON :
3132 rnd >= 1 && rnd <= 4 ? EL_PACMAN_RIGHT + rnd - 1 :
3133 rnd >= 5 && rnd <= 20 ? EL_POLAR_START + rnd - 5 :
3134 rnd >= 21 && rnd <= 24 ? EL_POLAR_CROSS_START + rnd - 21 :
3135 EL_MIRROR_FIXED_START + rnd - 25);
3140 graphic = el2gfx(element);
3147 BlitBitmap(pix[PIX_BACK], drawto,
3148 SX + (graphic % GFX_PER_LINE) * TILEX + x,
3149 SY + (graphic / GFX_PER_LINE) * TILEY + y, 6, 6,
3150 SX + ELX * TILEX + x,
3151 SY + ELY * TILEY + y);
3152 MarkTileDirty(ELX, ELY);
3155 DrawLaser(0, DL_LASER_ENABLED);
3160 Feld[ELX][ELY] = element;
3161 DrawField_MM(ELX, ELY);
3164 printf("NEW ELEMENT: (%d, %d)\n", ELX, ELY);
3167 /* above stuff: GRAY BALL -> PRISM !!! */
3169 LX = ELX * TILEX + 14;
3170 LY = ELY * TILEY + 14;
3171 if (laser.current_angle == (laser.current_angle >> 1) << 1)
3178 laser.num_edges -= 2;
3179 laser.num_damages--;
3183 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
3184 if (laser.damage[i].is_mirror)
3188 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3190 DrawLaser(0, DL_LASER_DISABLED);
3192 DrawLaser(0, DL_LASER_DISABLED);
3198 printf("TEST ELEMENT: %d\n", Feld[0][0]);
3205 if (IS_WALL_ICE(element) && CT > 1000)
3207 PlaySoundStereo(SND_SLURP, ST(ELX));
3212 Feld[ELX][ELY] = Feld[ELX][ELY] - EL_WALL_ICE + EL_WALL_CHANGING;
3213 Store[ELX][ELY] = EL_WALL_ICE;
3214 Store2[ELX][ELY] = laser.wall_mask;
3216 laser.dest_element = Feld[ELX][ELY];
3230 Feld[ELX][ELY] &= (laser.wall_mask ^ 0xFF);
3234 DrawWallsAnimation_MM(ELX, ELY, Feld[ELX][ELY], phase, laser.wall_mask);
3239 if (Feld[ELX][ELY] == EL_WALL_ICE)
3240 Feld[ELX][ELY] = EL_EMPTY;
3244 LX = laser.edge[laser.num_edges].x - (SX + 2);
3245 LY = laser.edge[laser.num_edges].y - (SY + 2);
3248 for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
3249 if (laser.damage[i].is_mirror)
3253 DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3255 DrawLaser(0, DL_LASER_DISABLED);
3262 if (IS_WALL_AMOEBA(element) && CT > 1200)
3264 int k1, k2, k3, dx, dy, de, dm;
3265 int element2 = Feld[ELX][ELY];
3267 if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element))
3270 for (i = laser.num_damages - 1; i>=0; i--)
3271 if (laser.damage[i].is_mirror)
3274 r = laser.num_edges;
3275 d = laser.num_damages;
3282 DrawLaser(laser.damage[k1].edge - 1, DL_LASER_DISABLED);
3285 DrawLaser(0, DL_LASER_ENABLED);
3288 x = laser.damage[k1].x;
3289 y = laser.damage[k1].y;
3295 if (laser.wall_mask & (1 << i))
3297 if (ReadPixel(drawto,
3298 SX + ELX * TILEX + 14 + (i % 2) * 2,
3299 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3301 if (ReadPixel(drawto,
3302 SX + ELX * TILEX + 31 * (i % 2),
3303 SY + ELY * TILEY + 14 + (i / 2) * 2) == pen_ray)
3312 if (laser.wall_mask & (1 << i))
3314 if (ReadPixel(drawto,
3315 SX + ELX * TILEX + 31 * (i % 2),
3316 SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3323 if (laser.num_beamers > 0 ||
3324 k1 < 1 || k2 < 4 || k3 < 4 ||
3325 ReadPixel(drawto, SX + ELX * TILEX + 14, SY + ELY * TILEY + 14)
3328 laser.num_edges = r;
3329 laser.num_damages = d;
3330 DrawLaser(0, DL_LASER_DISABLED);
3333 Feld[ELX][ELY] = element | laser.wall_mask;
3336 de = Feld[ELX][ELY];
3337 dm = laser.wall_mask;
3343 int x = ELX, y = ELY;
3344 int wall_mask = laser.wall_mask;
3348 DrawLaser(0, DL_LASER_ENABLED);
3350 PlaySoundStereo(SND_AMOEBE, ST(dx));
3354 Feld[x][y] = Feld[x][y] - EL_WALL_AMOEBA + EL_WALL_CHANGING;
3355 Store[x][y] = EL_WALL_AMOEBA;
3356 Store2[x][y] = wall_mask;
3364 DrawWallsAnimation_MM(dx, dy, de, 4, dm);
3366 DrawLaser(0, DL_LASER_ENABLED);
3368 PlaySoundStereo(SND_AMOEBE, ST(dx));
3372 DrawWallsAnimation_MM(dx, dy, de, i, dm);
3377 DrawLaser(0, DL_LASER_ENABLED);
3382 if ((element == EL_BLOCK_WOOD || element == EL_BLOCK_STONE) &&
3383 laser.stops_inside_element && CT > 1500)
3388 if (ABS(XS) > ABS(YS))
3402 x = ELX + Step[k * 4].x;
3403 y = ELY + Step[k * 4].y;
3405 if (!IN_LEV_FIELD(x, y) || Feld[x][y] != EL_EMPTY)
3408 if (ObjHit(x, y, HIT_POS_CENTER | HIT_POS_EDGE | HIT_POS_BETWEEN))
3416 laser.overloaded = (element == EL_BLOCK_STONE);
3420 PlaySoundStereo(SND_BONG, ST(ELX));
3423 Feld[x][y] = element;
3425 DrawGraphic_MM(ELX, ELY, -1);
3428 if (element == EL_BLOCK_STONE && Box[ELX][ELY])
3430 DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
3431 DrawLaser(laser.num_edges - 1, DL_LASER_ENABLED);
3439 if (element == EL_FUEL_FULL && CT > 200)
3441 for(i=game_mm.energy_left; i<=MAX_LASER_ENERGY; i+=2)
3443 BlitBitmap(pix[PIX_DOOR], drawto,
3444 DOOR_GFX_PAGEX4 + XX_ENERGY,
3445 DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
3446 ENERGY_XSIZE, i, DX_ENERGY,
3447 DY_ENERGY + ENERGY_YSIZE - i);
3449 redraw_mask |= REDRAW_DOOR_1;
3455 game_mm.energy_left = MAX_LASER_ENERGY;
3456 Feld[ELX][ELY] = EL_FUEL_EMPTY;
3457 DrawField_MM(ELX, ELY);
3459 DrawLaser(0, DL_LASER_ENABLED);
3470 int mx, my, ox, oy, nx, ny;
3474 if (++p >= game_mm.num_pacman)
3476 game_mm.pacman[p].dir--;
3480 game_mm.pacman[p].dir++;
3482 if (game_mm.pacman[p].dir > 4)
3483 game_mm.pacman[p].dir = 1;
3485 if (game_mm.pacman[p].dir % 2)
3488 my = game_mm.pacman[p].dir - 2;
3493 mx = 3 - game_mm.pacman[p].dir;
3496 ox = game_mm.pacman[p].x;
3497 oy = game_mm.pacman[p].y;
3500 element = Feld[nx][ny];
3501 if (nx < 0 || nx > 15 || ny < 0 || ny > 11)
3504 if (!IS_EATABLE4PACMAN(element))
3507 if (ObjHit(nx, ny, HIT_POS_CENTER))
3510 Feld[ox][oy] = EL_EMPTY;
3512 EL_PACMAN_RIGHT - 1 +
3513 (game_mm.pacman[p].dir - 1 +
3514 (game_mm.pacman[p].dir % 2) * 2);
3516 game_mm.pacman[p].x = nx;
3517 game_mm.pacman[p].y = ny;
3518 g = Feld[nx][ny] - EL_PACMAN_RIGHT;
3519 DrawGraphic_MM(ox, oy, GFX_EMPTY);
3521 if (element != EL_EMPTY)
3526 ox = SX + ox * TILEX;
3527 oy = SY + oy * TILEY;
3529 for(i=1; i<33; i+=2)
3531 BlitBitmap(pix[PIX_BACK], window,
3532 SX + g * TILEX, SY + 4 * TILEY, TILEX, TILEY,
3533 ox + i * mx, oy + i * my);
3539 Ct = Ct + Counter() - CT;
3541 DrawField_MM(nx, ny);
3544 if (!laser.fuse_off)
3546 DrawLaser(0, DL_LASER_ENABLED);
3548 if (ObjHit(nx, ny, HIT_POS_BETWEEN))
3550 AddDamagedField(nx, ny);
3551 laser.damage[laser.num_damages - 1].edge = 0;
3555 if (element == EL_BOMB)
3557 DeletePacMan(nx, ny);
3560 if (IS_WALL_AMOEBA(element) &&
3561 (LX + 2 * XS) / TILEX == nx &&
3562 (LY + 2 * YS) / TILEY == ny)
3574 boolean raise_level = FALSE;
3577 if (local_player->MovPos)
3580 local_player->LevelSolved = FALSE;
3583 if (game_mm.energy_left)
3585 if (setup.sound_loops)
3586 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT, SND_CTRL_PLAY_LOOP);
3588 while(game_mm.energy_left > 0)
3590 if (!setup.sound_loops)
3591 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3594 if (game_mm.energy_left > 0 && !(game_mm.energy_left % 10))
3595 RaiseScore(native_mm_level.score[SC_ZEITBONUS]);
3600 game_mm.energy_left--;
3601 if (game_mm.energy_left >= 0)
3603 BlitBitmap(pix[PIX_DOOR], drawto,
3604 DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
3605 ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
3606 DX_ENERGY, DY_ENERGY);
3607 redraw_mask |= REDRAW_DOOR_1;
3614 if (setup.sound_loops)
3615 StopSound(SND_SIRR);
3617 else if (native_mm_level.time == 0) /* level without time limit */
3619 if (setup.sound_loops)
3620 PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT, SND_CTRL_PLAY_LOOP);
3622 while(TimePlayed < 999)
3624 if (!setup.sound_loops)
3625 PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3626 if (TimePlayed < 999 && !(TimePlayed % 10))
3627 RaiseScore(native_mm_level.score[SC_ZEITBONUS]);
3628 if (TimePlayed < 900 && !(TimePlayed % 10))
3634 DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
3641 if (setup.sound_loops)
3642 StopSound(SND_SIRR);
3649 CloseDoor(DOOR_CLOSE_1);
3651 Request("Level solved !", REQ_CONFIRM);
3653 if (level_nr == leveldir_current->handicap_level)
3655 leveldir_current->handicap_level++;
3656 SaveLevelSetup_SeriesInfo();
3659 if (level_editor_test_game)
3660 game_mm.score = -1; /* no highscore when playing from editor */
3661 else if (level_nr < leveldir_current->last_level)
3662 raise_level = TRUE; /* advance to next level */
3664 if ((hi_pos = NewHiScore()) >= 0)
3666 game_status = HALLOFFAME;
3667 // DrawHallOfFame(hi_pos);
3673 game_status = MAINMENU;
3687 // LoadScore(level_nr);
3689 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
3690 game_mm.score < highscore[MAX_SCORE_ENTRIES - 1].Score)
3693 for (k=0; k<MAX_SCORE_ENTRIES; k++)
3695 if (game_mm.score > highscore[k].Score)
3697 /* player has made it to the hall of fame */
3699 if (k < MAX_SCORE_ENTRIES - 1)
3701 int m = MAX_SCORE_ENTRIES - 1;
3704 for (l=k; l<MAX_SCORE_ENTRIES; l++)
3705 if (!strcmp(setup.player_name, highscore[l].Name))
3707 if (m == k) /* player's new highscore overwrites his old one */
3713 strcpy(highscore[l].Name, highscore[l - 1].Name);
3714 highscore[l].Score = highscore[l - 1].Score;
3721 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3722 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3723 highscore[k].Score = game_mm.score;
3729 else if (!strncmp(setup.player_name, highscore[k].Name,
3730 MAX_PLAYER_NAME_LEN))
3731 break; /* player already there with a higher score */
3736 // if (position >= 0)
3737 // SaveScore(level_nr);
3742 void InitMovingField(int x, int y, int direction)
3744 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3745 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3747 MovDir[x][y] = direction;
3748 MovDir[newx][newy] = direction;
3749 if (Feld[newx][newy] == EL_EMPTY)
3750 Feld[newx][newy] = EL_BLOCKED;
3753 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3755 int direction = MovDir[x][y];
3756 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3757 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3763 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3765 int oldx = x, oldy = y;
3766 int direction = MovDir[x][y];
3768 if (direction == MV_LEFT)
3770 else if (direction == MV_RIGHT)
3772 else if (direction == MV_UP)
3774 else if (direction == MV_DOWN)
3777 *comes_from_x = oldx;
3778 *comes_from_y = oldy;
3781 int MovingOrBlocked2Element(int x, int y)
3783 int element = Feld[x][y];
3785 if (element == EL_BLOCKED)
3789 Blocked2Moving(x, y, &oldx, &oldy);
3790 return Feld[oldx][oldy];
3797 static void RemoveField(int x, int y)
3799 Feld[x][y] = EL_EMPTY;
3806 void RemoveMovingField(int x, int y)
3808 int oldx = x, oldy = y, newx = x, newy = y;
3810 if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
3813 if (IS_MOVING(x, y))
3815 Moving2Blocked(x, y, &newx, &newy);
3816 if (Feld[newx][newy] != EL_BLOCKED)
3819 else if (Feld[x][y] == EL_BLOCKED)
3821 Blocked2Moving(x, y, &oldx, &oldy);
3822 if (!IS_MOVING(oldx, oldy))
3826 Feld[oldx][oldy] = EL_EMPTY;
3827 Feld[newx][newy] = EL_EMPTY;
3828 MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
3829 MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
3831 DrawLevelField_MM(oldx, oldy);
3832 DrawLevelField_MM(newx, newy);
3835 void PlaySoundLevel(int x, int y, int sound_nr)
3837 int sx = SCREENX(x), sy = SCREENY(y);
3839 int silence_distance = 8;
3841 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
3842 (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
3845 if (!IN_LEV_FIELD(x, y) ||
3846 sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
3847 sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
3850 volume = SOUND_MAX_VOLUME;
3853 stereo = (sx - SCR_FIELDX/2) * 12;
3855 stereo = SOUND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
3856 if (stereo > SOUND_MAX_RIGHT)
3857 stereo = SOUND_MAX_RIGHT;
3858 if (stereo < SOUND_MAX_LEFT)
3859 stereo = SOUND_MAX_LEFT;
3862 if (!IN_SCR_FIELD(sx, sy))
3864 int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
3865 int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
3867 volume -= volume * (dx > dy ? dx : dy) / silence_distance;
3870 PlaySoundExt(sound_nr, volume, stereo, SND_CTRL_PLAY_SOUND);
3873 void RaiseScore(int value)
3875 game_mm.score += value;
3877 DrawText(DX_SCORE, DY_SCORE, int2str(game_mm.score, 4),
3882 void RaiseScoreElement(int element)
3887 RaiseScore(native_mm_level.score[SC_PACMAN]);
3890 RaiseScore(native_mm_level.score[SC_KEY]);