enabled initial charging of laser energy (MM engine)
[rocksndiamonds.git] / src / game_mm / mm_game.c
1 // ============================================================================
2 // Mirror Magic -- McDuffin's Revenge
3 // ----------------------------------------------------------------------------
4 // (c) 1994-2017 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // mm_game.c
10 // ============================================================================
11
12 #include "main_mm.h"
13
14 #include "mm_main.h"
15 #include "mm_game.h"
16 #include "mm_tools.h"
17
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
23
24 /* values for Explode_MM() */
25 #define EX_PHASE_START          0
26 #define EX_NORMAL               0
27 #define EX_KETTLE               1
28 #define EX_SHORT                2
29
30 /* special positions in the game control window (relative to control window) */
31 #define XX_LEVEL                36
32 #define YY_LEVEL                23
33 #define XX_KETTLES              29
34 #define YY_KETTLES              63
35 #define XX_SCORE                22
36 #define YY_SCORE                101
37 #define XX_ENERGY               8
38 #define YY_ENERGY               158
39 #define XX_OVERLOAD             60
40 #define YY_OVERLOAD             YY_ENERGY
41
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)
53
54 #define IS_LOOP_SOUND(s)        ((s) == SND_FUEL)
55 #define IS_MUSIC_SOUND(s)       ((s) == SND_TYGER || (s) == SND_VOYAGER)
56
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
61
62 #define NUM_GAME_BUTTONS        3
63
64 /* values for DrawLaser() */
65 #define DL_LASER_DISABLED       0
66 #define DL_LASER_ENABLED        1
67
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
72
73 /* forward declaration for internal use */
74 static int MovingOrBlocked2Element_MM(int, int);
75 static void Bang_MM(int, int);
76 static void RaiseScore_MM(int);
77 static void RemoveMovingField_MM(int, int);
78 static void InitMovingField_MM(int, int, int);
79 static void ContinueMoving_MM(int, int);
80 static void Moving2Blocked_MM(int, int, int *, int *);
81
82
83 static int get_element_angle(int element)
84 {
85   int element_phase = get_element_phase(element);
86
87   if (IS_MIRROR_FIXED(element) ||
88       IS_MCDUFFIN(element) ||
89       IS_LASER(element) ||
90       IS_RECEIVER(element))
91     return 4 * element_phase;
92   else
93     return element_phase;
94 }
95
96 static int get_opposite_angle(int angle)
97 {
98   int opposite_angle = angle + ANG_RAY_180;
99
100   /* make sure "opposite_angle" is in valid interval [0, 15] */
101   return (opposite_angle + 16) % 16;
102 }
103
104 static int get_mirrored_angle(int laser_angle, int mirror_angle)
105 {
106   int reflected_angle = 16 - laser_angle + mirror_angle;
107
108   /* make sure "reflected_angle" is in valid interval [0, 15] */
109   return (reflected_angle + 16) % 16;
110 }
111
112 static void InitMovDir_MM(int x, int y)
113 {
114   int element = Feld[x][y];
115   static int direction[3][4] =
116   {
117     { MV_RIGHT, MV_UP,    MV_LEFT,  MV_DOWN },
118     { MV_LEFT,  MV_DOWN,  MV_RIGHT, MV_UP   },
119     { MV_LEFT,  MV_RIGHT, MV_UP,    MV_DOWN }
120   };
121
122   switch(element)
123   {
124     case EL_PACMAN_RIGHT:
125     case EL_PACMAN_UP:
126     case EL_PACMAN_LEFT:
127     case EL_PACMAN_DOWN:
128       Feld[x][y] = EL_PACMAN;
129       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
130       break;
131
132     default:
133       break;
134   }
135 }
136
137 static void InitField(int x, int y, boolean init_game)
138 {
139   int element = Feld[x][y];
140
141   switch (element)
142   {
143     case EL_DF_EMPTY:
144       Feld[x][y] = EL_EMPTY;
145       break;
146
147     case EL_KETTLE:
148     case EL_CELL:
149       if (native_mm_level.auto_count_kettles)
150         game_mm.kettles_still_needed++;
151       break;
152
153     case EL_LIGHTBULB_OFF:
154       game_mm.lights_still_needed++;
155       break;
156
157     default:
158       if (IS_MIRROR(element) ||
159           IS_BEAMER_OLD(element) ||
160           IS_BEAMER(element) ||
161           IS_POLAR(element) ||
162           IS_POLAR_CROSS(element) ||
163           IS_DF_MIRROR(element) ||
164           IS_DF_MIRROR_AUTO(element) ||
165           IS_GRID_STEEL_AUTO(element) ||
166           IS_GRID_WOOD_AUTO(element) ||
167           IS_FIBRE_OPTIC(element))
168       {
169         if (IS_BEAMER_OLD(element))
170         {
171           Feld[x][y] = EL_BEAMER_BLUE_START + (element - EL_BEAMER_START);
172           element = Feld[x][y];
173         }
174
175         if (!IS_FIBRE_OPTIC(element))
176         {
177           static int steps_grid_auto = 0;
178
179           if (game_mm.num_cycle == 0)   /* initialize cycle steps for grids */
180             steps_grid_auto = RND(16) * (RND(2) ? -1 : +1);
181
182           if (IS_GRID_STEEL_AUTO(element) ||
183               IS_GRID_WOOD_AUTO(element))
184             game_mm.cycle[game_mm.num_cycle].steps = steps_grid_auto;
185           else
186             game_mm.cycle[game_mm.num_cycle].steps = RND(16) * (RND(2) ? -1 : +1);
187
188           game_mm.cycle[game_mm.num_cycle].x = x;
189           game_mm.cycle[game_mm.num_cycle].y = y;
190           game_mm.num_cycle++;
191         }
192
193         if (IS_BEAMER(element) || IS_FIBRE_OPTIC(element))
194         {
195           int beamer_nr = BEAMER_NR(element);
196           int nr = laser.beamer[beamer_nr][0].num;
197
198           laser.beamer[beamer_nr][nr].x = x;
199           laser.beamer[beamer_nr][nr].y = y;
200           laser.beamer[beamer_nr][nr].num = 1;
201         }
202       }
203       else if (IS_PACMAN(element))
204       {
205         InitMovDir_MM(x, y);
206       }
207       else if (IS_MCDUFFIN(element) || IS_LASER(element))
208       {
209         laser.start_edge.x = x;
210         laser.start_edge.y = y;
211         laser.start_angle = get_element_angle(element);
212       }
213
214       break;
215   }
216 }
217
218 static void InitCycleElements()
219 {
220   int i, j;
221
222   if (game_mm.num_cycle == 0)   /* no elements to cycle */
223     return;
224
225   for (i = 0; i < 16; i++)
226   {
227     for (j = 0; j < game_mm.num_cycle; j++)
228     {
229       int x = game_mm.cycle[j].x;
230       int y = game_mm.cycle[j].y;
231       int step = SIGN(game_mm.cycle[j].steps);
232       int last_element = Feld[x][y];
233       int next_element = get_rotated_element(last_element, step);
234
235       if (!game_mm.cycle[j].steps)
236         continue;
237
238       Feld[x][y] = next_element;
239
240       DrawField_MM(x, y);
241       game_mm.cycle[j].steps -= step;
242     }
243
244     BackToFront();
245     ColorCycling();
246
247 #ifdef DEBUG
248     if (setup.quick_doors)
249       continue;
250 #endif
251
252     Delay(AUTO_ROTATE_DELAY);
253   }
254 }
255
256 static void InitLaser()
257 {
258   int start_element = Feld[laser.start_edge.x][laser.start_edge.y];
259   int step = (IS_LASER(start_element) ? 4 : 0);
260
261   LX = laser.start_edge.x * TILEX;
262   if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
263     LX += 14;
264   else
265     LX += (laser.start_angle == ANG_RAY_RIGHT ? 28 + step : 0 - step);
266
267   LY = laser.start_edge.y * TILEY;
268   if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
269     LY += (laser.start_angle == ANG_RAY_DOWN ? 28 + step : 0 - step);
270   else
271     LY += 14;
272
273   XS = 2 * Step[laser.start_angle].x;
274   YS = 2 * Step[laser.start_angle].y;
275
276   laser.current_angle = laser.start_angle;
277
278   laser.num_damages = 0;
279   laser.num_edges = 0;
280   laser.num_beamers = 0;
281   laser.beamer_edge[0] = 0;
282
283   AddLaserEdge(LX, LY);         /* set laser starting edge */
284
285   pen_ray = GetPixelFromRGB(window,
286                             native_mm_level.laser_red   * 0xFF,
287                             native_mm_level.laser_green * 0xFF,
288                             native_mm_level.laser_blue  * 0xFF);
289 }
290
291 void InitGameEngine_MM()
292 {
293   int i, x, y;
294
295   /* set global game control values */
296   game_mm.num_cycle = 0;
297   game_mm.num_pacman = 0;
298
299   game_mm.score = 0;
300   game_mm.energy_left = 0;      // later set to "native_mm_level.time"
301   game_mm.kettles_still_needed =
302     (native_mm_level.auto_count_kettles ? 0 : native_mm_level.kettles_needed);
303   game_mm.lights_still_needed = 0;
304   game_mm.num_keys = 0;
305
306   game_mm.level_solved = FALSE;
307   game_mm.game_over = FALSE;
308   game_mm.game_over_cause = 0;
309
310   game_mm.laser_overload_value = 0;
311
312   /* set global laser control values (must be set before "InitLaser()") */
313   laser.start_edge.x = 0;
314   laser.start_edge.y = 0;
315   laser.start_angle = 0;
316
317   for (i = 0; i < MAX_NUM_BEAMERS; i++)
318     laser.beamer[i][0].num = laser.beamer[i][1].num = 0;
319
320   laser.overloaded = FALSE;
321   laser.overload_value = 0;
322   laser.fuse_off = FALSE;
323   laser.fuse_x = laser.fuse_y = -1;
324
325   laser.dest_element = EL_EMPTY;
326   laser.wall_mask = 0;
327
328   CT = Ct = 0;
329
330   for (x = 0; x < lev_fieldx; x++)
331   {
332     for (y = 0; y < lev_fieldy; y++)
333     {
334       Feld[x][y] = Ur[x][y];
335       Hit[x][y] = Box[x][y] = 0;
336       Angle[x][y] = 0;
337       MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
338       Store[x][y] = Store2[x][y] = 0;
339       Frame[x][y] = 0;
340       Stop[x][y] = FALSE;
341
342       InitField(x, y, TRUE);
343     }
344   }
345
346 #if 0
347   CloseDoor(DOOR_CLOSE_1);
348 #endif
349
350   DrawLevel_MM();
351 }
352
353 void InitGameActions_MM()
354 {
355   int i;
356
357   InitCycleElements();
358   InitLaser();
359
360 #if 0
361   /* copy default game door content to main double buffer */
362   BlitBitmap(pix[PIX_DOOR], drawto,
363              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
364 #endif
365
366 #if 0
367   DrawText(DX_LEVEL, DY_LEVEL,
368            int2str(level_nr, 2), FONT_TEXT_2);
369   DrawText(DX_KETTLES, DY_KETTLES,
370            int2str(game_mm.kettles_still_needed, 3), FONT_TEXT_2);
371   DrawText(DX_SCORE, DY_SCORE,
372            int2str(game_mm.score, 4), FONT_TEXT_2);
373 #endif
374
375 #if 0
376   UnmapGameButtons();
377   MapGameButtons();
378 #endif
379
380 #if 0
381   /* copy actual game door content to door double buffer for OpenDoor() */
382   BlitBitmap(drawto, pix[PIX_DB_DOOR],
383              DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
384 #endif
385
386 #if 0
387   OpenDoor(DOOR_OPEN_ALL);
388 #endif
389
390   if (setup.sound_loops)
391     PlaySoundExt(SND_FUEL, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT, SND_CTRL_PLAY_LOOP);
392
393   for (i = 0; i <= native_mm_level.time; i += 2)
394   {
395     if (!setup.sound_loops)
396       PlaySoundStereo(SND_FUEL, SOUND_MAX_RIGHT);
397
398     game_mm.energy_left = i;
399
400     UpdateAndDisplayGameControlValues();
401
402     BackToFront();
403
404     ColorCycling();
405
406 #ifdef DEBUG
407     if (setup.quick_doors)
408       continue;
409 #endif
410   }
411
412   game_mm.energy_left = native_mm_level.time;
413
414   if (setup.sound_loops)
415     StopSound(SND_FUEL);
416
417 #if 0
418   if (setup.sound_music && num_bg_loops)
419     PlayMusic(level_nr % num_bg_loops);
420 #endif
421
422   ScanLaser();
423 }
424
425 void AddLaserEdge(int lx, int ly)
426 {
427   if (lx < -2 || ly < -2 || lx >= SXSIZE + 2 || ly >= SYSIZE + 2)
428   {
429     Error(ERR_WARN, "AddLaserEdge: out of bounds: %d, %d", lx, ly);
430
431     return;
432   }
433
434   laser.edge[laser.num_edges].x = SX + 2 + lx;
435   laser.edge[laser.num_edges].y = SY + 2 + ly;
436   laser.num_edges++;
437
438   laser.redraw = TRUE;
439 }
440
441 void AddDamagedField(int ex, int ey)
442 {
443   laser.damage[laser.num_damages].is_mirror = FALSE;
444   laser.damage[laser.num_damages].angle = laser.current_angle;
445   laser.damage[laser.num_damages].edge = laser.num_edges;
446   laser.damage[laser.num_damages].x = ex;
447   laser.damage[laser.num_damages].y = ey;
448   laser.num_damages++;
449 }
450
451 boolean StepBehind()
452 {
453   if (laser.num_edges)
454   {
455     int x = LX - XS;
456     int y = LY - YS;
457     int last_x = laser.edge[laser.num_edges - 1].x - SX - 2;
458     int last_y = laser.edge[laser.num_edges - 1].y - SY - 2;
459
460     return ((x - last_x) * XS < 0 || (y - last_y) * YS < 0);
461   }
462
463   return FALSE;
464 }
465
466 static int getMaskFromElement(int element)
467 {
468   if (IS_GRID(element))
469     return IMG_MM_MASK_GRID_1 + get_element_phase(element);
470   else if (IS_MCDUFFIN(element))
471     return IMG_MM_MASK_MCDUFFIN_RIGHT + get_element_phase(element);
472   else if (IS_RECTANGLE(element) || IS_DF_GRID(element))
473     return IMG_MM_MASK_RECTANGLE;
474   else
475     return IMG_MM_MASK_CIRCLE;
476 }
477
478 int ScanPixel()
479 {
480   int hit_mask = 0;
481
482 #if 0
483   printf("ScanPixel: start scanning at (%d, %d) [%d, %d] [%d, %d]\n",
484          LX, LY, LX / TILEX, LY / TILEY, LX % TILEX, LY % TILEY);
485 #endif
486
487   /* follow laser beam until it hits something (at least the screen border) */
488   while (hit_mask == HIT_MASK_NO_HIT)
489   {
490     int i;
491
492 #if 0
493     /* for safety */
494     if (SX + LX < REAL_SX || SX + LX >= REAL_SX + FULL_SXSIZE ||
495         SY + LY < REAL_SY || SY + LY >= REAL_SY + FULL_SYSIZE)
496     {
497       printf("ScanPixel: touched screen border!\n");
498
499       return HIT_MASK_ALL;
500     }
501 #endif
502
503     for (i = 0; i < 4; i++)
504     {
505       int px = LX + (i % 2) * 2;
506       int py = LY + (i / 2) * 2;
507       int dx = px % TILEX;
508       int dy = py % TILEY;
509       int lx = (px + TILEX) / TILEX - 1;  /* ...+TILEX...-1 to get correct */
510       int ly = (py + TILEY) / TILEY - 1;  /* negative values!              */
511       Pixel pixel;
512
513       if (IN_LEV_FIELD(lx, ly))
514       {
515         int element = Feld[lx][ly];
516
517         if (element == EL_EMPTY || element == EL_EXPLODING_TRANSP)
518         {
519           pixel = 0;
520         }
521         else if (IS_WALL(element) || IS_WALL_CHANGING(element))
522         {
523           int pos = dy / MINI_TILEY * 2 + dx / MINI_TILEX;
524
525           pixel = ((element & (1 << pos)) ? 1 : 0);
526         }
527         else
528         {
529           int graphic_mask = getMaskFromElement(element);
530           Bitmap *bitmap;
531           int src_x, src_y;
532           int mask_x, mask_y;
533
534           getGraphicSource(graphic_mask, 0, &bitmap, &src_x, &src_y);
535
536           mask_x = src_x + dx;
537           mask_y = src_y + dy;
538
539           pixel = (ReadPixel(bitmap, mask_x, mask_y) ? 1 : 0);
540         }
541       }
542       else
543       {
544         pixel = (SX + px < REAL_SX || SX + px >= REAL_SX + FULL_SXSIZE ||
545                  SY + py < REAL_SY || SY + py >= REAL_SY + FULL_SYSIZE);
546       }
547
548       if ((Sign[laser.current_angle] & (1 << i)) && pixel)
549         hit_mask |= (1 << i);
550     }
551
552     if (hit_mask == HIT_MASK_NO_HIT)
553     {
554       /* hit nothing -- go on with another step */
555       LX += XS;
556       LY += YS;
557     }
558   }
559
560   return hit_mask;
561 }
562
563 void ScanLaser()
564 {
565   int element;
566   int end = 0, rf = laser.num_edges;
567
568   /* do not scan laser again after the game was lost for whatever reason */
569   if (game_mm.game_over)
570     return;
571
572   laser.overloaded = FALSE;
573   laser.stops_inside_element = FALSE;
574
575   DrawLaser(0, DL_LASER_ENABLED);
576
577 #if 0
578   printf("Start scanning with LX == %d, LY == %d, XS == %d, YS == %d\n",
579          LX, LY, XS, YS);
580 #endif
581
582   while (1)
583   {
584     int hit_mask;
585
586     if (laser.num_edges > MAX_LASER_LEN || laser.num_damages > MAX_LASER_LEN)
587     {
588       end = 1;
589       laser.overloaded = TRUE;
590
591       break;
592     }
593
594     hit_mask = ScanPixel();
595
596 #if 0
597     printf("Hit something at LX == %d, LY == %d, XS == %d, YS == %d\n",
598            LX, LY, XS, YS);
599 #endif
600
601     /* hit something -- check out what it was */
602     ELX = (LX + XS) / TILEX;
603     ELY = (LY + YS) / TILEY;
604
605 #if 0
606     printf("hit_mask (1) == '%x' (%d, %d) (%d, %d)\n",
607            hit_mask, LX, LY, ELX, ELY);
608 #endif
609
610     if (!IN_LEV_FIELD(ELX, ELY) || !IN_PIX_FIELD(LX, LY))
611     {
612       element = EL_EMPTY;
613       laser.dest_element = element;
614
615       break;
616     }
617
618     if (hit_mask == (HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT))
619     {
620       /* we have hit the top-right and bottom-left element --
621          choose the bottom-left one */
622       /* !!! THIS CAN BE DONE MORE INTELLIGENTLY, FOR EXAMPLE, IF ONE
623          ELEMENT WAS STEEL AND THE OTHER ONE WAS ICE => ALWAYS CHOOSE
624          THE ICE AND MELT IT AWAY INSTEAD OF OVERLOADING LASER !!! */
625       ELX = (LX - 2) / TILEX;
626       ELY = (LY + 2) / TILEY;
627     }
628
629     if (hit_mask == (HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT))
630     {
631       /* we have hit the top-left and bottom-right element --
632          choose the top-left one */
633       /* !!! SEE ABOVE !!! */
634       ELX = (LX - 2) / TILEX;
635       ELY = (LY - 2) / TILEY;
636     }
637
638 #if 0
639     printf("hit_mask (2) == '%x' (%d, %d) (%d, %d)\n",
640            hit_mask, LX, LY, ELX, ELY);
641 #endif
642
643     element = Feld[ELX][ELY];
644     laser.dest_element = element;
645
646 #if 0
647     printf("Hit element %d at (%d, %d) [%d, %d] [%d, %d] [%d]\n",
648            element, ELX, ELY,
649            LX, LY,
650            LX % TILEX, LY % TILEY,
651            hit_mask);
652 #endif
653
654 #if 0
655     if (!IN_LEV_FIELD(ELX, ELY))
656       printf("WARNING! (1) %d, %d (%d)\n", ELX, ELY, element);
657 #endif
658
659     if (element == EL_EMPTY)
660     {
661       if (!HitOnlyAnEdge(element, hit_mask))
662         break;
663     }
664     else if (element == EL_FUSE_ON)
665     {
666       if (HitPolarizer(element, hit_mask))
667         break;
668     }
669     else if (IS_GRID(element) || IS_DF_GRID(element))
670     {
671       if (HitPolarizer(element, hit_mask))
672         break;
673     }
674     else if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD ||
675              element == EL_GATE_STONE || element == EL_GATE_WOOD)
676     {
677       if (HitBlock(element, hit_mask))
678       {
679         rf = 1;
680
681         break;
682       }
683     }
684     else if (IS_MCDUFFIN(element))
685     {
686       if (HitLaserSource(element, hit_mask))
687         break;
688     }
689     else if ((element >= EL_EXIT_CLOSED && element <= EL_EXIT_OPEN) ||
690              IS_RECEIVER(element))
691     {
692       if (HitLaserDestination(element, hit_mask))
693         break;
694     }
695     else if (IS_WALL(element))
696     {
697       if (IS_WALL_STEEL(element) || IS_DF_WALL_STEEL(element))
698       {
699         if (HitReflectingWalls(element, hit_mask))
700           break;
701       }
702       else
703       {
704         if (HitAbsorbingWalls(element, hit_mask))
705           break;
706       }
707     }
708     else
709     {
710       if (HitElement(element, hit_mask))
711         break;
712     }
713
714     if (rf)
715       DrawLaser(rf - 1, DL_LASER_ENABLED);
716     rf = laser.num_edges;
717   }
718
719 #if 0
720   if (laser.dest_element != Feld[ELX][ELY])
721   {
722     printf("ALARM: laser.dest_element == %d, Feld[ELX][ELY] == %d\n",
723            laser.dest_element, Feld[ELX][ELY]);
724   }
725 #endif
726
727   if (!end && !laser.stops_inside_element && !StepBehind())
728   {
729 #if 0
730     printf("ScanLaser: Go one step back\n");
731 #endif
732
733     LX -= XS;
734     LY -= YS;
735
736     AddLaserEdge(LX, LY);
737   }
738
739   if (rf)
740     DrawLaser(rf - 1, DL_LASER_ENABLED);
741
742   Ct = CT = Counter();
743
744 #if 0
745     if (!IN_LEV_FIELD(ELX, ELY))
746       printf("WARNING! (2) %d, %d\n", ELX, ELY);
747 #endif
748 }
749
750 void DrawLaserExt(int start_edge, int num_edges, int mode)
751 {
752   int element;
753   int elx, ely;
754
755 #if 0
756   printf("DrawLaserExt: start_edge, num_edges, mode == %d, %d, %d\n",
757          start_edge, num_edges, mode);
758 #endif
759
760   if (start_edge < 0)
761   {
762     Error(ERR_WARN, "DrawLaserExt: start_edge < 0");
763
764     return;
765   }
766
767   if (num_edges < 0)
768   {
769     Error(ERR_WARN, "DrawLaserExt: num_edges < 0");
770
771     return;
772   }
773
774 #if 0
775   if (mode == DL_LASER_DISABLED)
776   {
777     printf("DrawLaser: Delete laser from edge %d\n", start_edge);
778   }
779 #endif
780
781   /* now draw the laser to the backbuffer and (if enabled) to the screen */
782   DrawLines(drawto, &laser.edge[start_edge], num_edges,
783             (mode == DL_LASER_ENABLED ? pen_ray : pen_bg));
784
785   redraw_mask |= REDRAW_FIELD;
786
787   if (mode == DL_LASER_ENABLED)
788     return;
789
790   /* after the laser was deleted, the "damaged" graphics must be restored */
791   if (laser.num_damages)
792   {
793     int damage_start = 0;
794     int i;
795
796     /* determine the starting edge, from which graphics need to be restored */
797     if (start_edge > 0)
798     {
799       for (i = 0; i < laser.num_damages; i++)
800       {
801         if (laser.damage[i].edge == start_edge + 1)
802         {
803           damage_start = i;
804
805           break;
806         }
807       }
808     }
809
810     /* restore graphics from this starting edge to the end of damage list */
811     for (i = damage_start; i < laser.num_damages; i++)
812     {
813       int lx = laser.damage[i].x;
814       int ly = laser.damage[i].y;
815       int element = Feld[lx][ly];
816
817       if (Hit[lx][ly] == laser.damage[i].edge)
818         if (!((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
819                i == damage_start))
820           Hit[lx][ly] = 0;
821       if (Box[lx][ly] == laser.damage[i].edge)
822         Box[lx][ly] = 0;
823
824       if (IS_DRAWABLE(element))
825         DrawField_MM(lx, ly);
826     }
827
828     elx = laser.damage[damage_start].x;
829     ely = laser.damage[damage_start].y;
830     element = Feld[elx][ely];
831
832 #if 0
833     if (IS_BEAMER(element))
834     {
835       int i;
836
837       for (i = 0; i < laser.num_beamers; i++)
838         printf("-> %d\n", laser.beamer_edge[i]);
839       printf("DrawLaserExt: IS_BEAMER: [%d]: Hit[%d][%d] == %d [%d]\n",
840              mode, elx, ely, Hit[elx][ely], start_edge);
841       printf("DrawLaserExt: IS_BEAMER: %d / %d\n",
842              get_element_angle(element), laser.damage[damage_start].angle);
843     }
844 #endif
845
846     if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
847         laser.num_beamers > 0 &&
848         start_edge == laser.beamer_edge[laser.num_beamers - 1])
849     {
850       /* element is outgoing beamer */
851       laser.num_damages = damage_start + 1;
852
853       if (IS_BEAMER(element))
854         laser.current_angle = get_element_angle(element);
855     }
856     else
857     {
858       /* element is incoming beamer or other element */
859       laser.num_damages = damage_start;
860       laser.current_angle = laser.damage[laser.num_damages].angle;
861     }
862   }
863   else
864   {
865     /* no damages but McDuffin himself (who needs to be redrawn anyway) */
866
867     elx = laser.start_edge.x;
868     ely = laser.start_edge.y;
869     element = Feld[elx][ely];
870   }
871
872   laser.num_edges = start_edge + 1;
873   if (start_edge == 0)
874     laser.current_angle = laser.start_angle;
875
876   LX = laser.edge[start_edge].x - (SX + 2);
877   LY = laser.edge[start_edge].y - (SY + 2);
878   XS = 2 * Step[laser.current_angle].x;
879   YS = 2 * Step[laser.current_angle].y;
880
881 #if 0
882   printf("DrawLaser: Set (LX, LY) to (%d, %d) [%d]\n",
883          LX, LY, element);
884 #endif
885
886   if (start_edge > 0)
887   {
888     if (IS_BEAMER(element) ||
889         IS_FIBRE_OPTIC(element) ||
890         IS_PACMAN(element) ||
891         IS_POLAR(element) ||
892         IS_POLAR_CROSS(element) ||
893         element == EL_FUSE_ON)
894     {
895       int step_size;
896
897 #if 0
898       printf("element == %d\n", element);
899 #endif
900
901       if (IS_22_5_ANGLE(laser.current_angle))   /* neither 90° nor 45° angle */
902         step_size = ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) ? 4 : 3);
903       else
904         step_size = 8;
905
906       if (IS_POLAR(element) || IS_POLAR_CROSS(element) ||
907           ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
908            (laser.num_beamers == 0 ||
909             start_edge != laser.beamer_edge[laser.num_beamers - 1])))
910       {
911         /* element is incoming beamer or other element */
912         step_size = -step_size;
913         laser.num_edges--;
914       }
915
916 #if 0
917       if (IS_BEAMER(element))
918       {
919         printf("start_edge == %d, laser.beamer_edge == %d\n",
920                start_edge, laser.beamer_edge);
921       }
922 #endif
923
924       LX += step_size * XS;
925       LY += step_size * YS;
926     }
927     else if (element != EL_EMPTY)
928     {
929       LX -= 3 * XS;
930       LY -= 3 * YS;
931       laser.num_edges--;
932     }
933   }
934
935 #if 0
936   printf("DrawLaser: Finally: (LX, LY) to (%d, %d) [%d]\n",
937          LX, LY, element);
938 #endif
939 }
940
941 void DrawLaser(int start_edge, int mode)
942 {
943   if (laser.num_edges - start_edge < 0)
944   {
945     Error(ERR_WARN, "DrawLaser: laser.num_edges - start_edge < 0");
946
947     return;
948   }
949
950   /* check if laser is interrupted by beamer element */
951   if (laser.num_beamers > 0 &&
952       start_edge < laser.beamer_edge[laser.num_beamers - 1])
953   {
954     if (mode == DL_LASER_ENABLED)
955     {
956       int i;
957       int tmp_start_edge = start_edge;
958
959       /* draw laser segments forward from the start to the last beamer */
960       for (i = 0; i < laser.num_beamers; i++)
961       {
962         int tmp_num_edges = laser.beamer_edge[i] - tmp_start_edge;
963
964         if (tmp_num_edges <= 0)
965           continue;
966
967 #if 0
968         printf("DrawLaser: DL_LASER_ENABLED: i==%d: %d, %d\n",
969                i, laser.beamer_edge[i], tmp_start_edge);
970 #endif
971
972         DrawLaserExt(tmp_start_edge, tmp_num_edges, DL_LASER_ENABLED);
973
974         tmp_start_edge = laser.beamer_edge[i];
975       }
976
977       /* draw last segment from last beamer to the end */
978       DrawLaserExt(tmp_start_edge, laser.num_edges - tmp_start_edge,
979                    DL_LASER_ENABLED);
980     }
981     else
982     {
983       int i;
984       int last_num_edges = laser.num_edges;
985       int num_beamers = laser.num_beamers;
986
987       /* delete laser segments backward from the end to the first beamer */
988       for (i = num_beamers-1; i >= 0; i--)
989       {
990         int tmp_num_edges = last_num_edges - laser.beamer_edge[i];
991
992         if (laser.beamer_edge[i] - start_edge <= 0)
993           break;
994
995         DrawLaserExt(laser.beamer_edge[i], tmp_num_edges, DL_LASER_DISABLED);
996
997         last_num_edges = laser.beamer_edge[i];
998         laser.num_beamers--;
999       }
1000
1001 #if 0
1002       if (last_num_edges - start_edge <= 0)
1003         printf("DrawLaser: DL_LASER_DISABLED: %d, %d\n",
1004                last_num_edges, start_edge);
1005 #endif
1006
1007       /* delete first segment from start to the first beamer */
1008       DrawLaserExt(start_edge, last_num_edges - start_edge, DL_LASER_DISABLED);
1009     }
1010   }
1011   else
1012   {
1013     DrawLaserExt(start_edge, laser.num_edges - start_edge, mode);
1014   }
1015 }
1016
1017 boolean HitElement(int element, int hit_mask)
1018 {
1019   if (HitOnlyAnEdge(element, hit_mask))
1020     return FALSE;
1021
1022   if (IS_MOVING(ELX, ELY) || IS_BLOCKED(ELX, ELY))
1023     element = MovingOrBlocked2Element_MM(ELX, ELY);
1024
1025 #if 0
1026   printf("HitElement (1): element == %d\n", element);
1027 #endif
1028
1029 #if 0
1030   if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1031     printf("HitElement (%d): EXACT MATCH @ (%d, %d)\n", element, ELX, ELY);
1032   else
1033     printf("HitElement (%d): FUZZY MATCH @ (%d, %d)\n", element, ELX, ELY);
1034 #endif
1035
1036   AddDamagedField(ELX, ELY);
1037
1038   /* this is more precise: check if laser would go through the center */
1039   if ((ELX * TILEX + 14 - LX) * YS != (ELY * TILEY + 14 - LY) * XS)
1040   {
1041     /* skip the whole element before continuing the scan */
1042     do
1043     {
1044       LX += XS;
1045       LY += YS;
1046     }
1047     while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1048
1049     if (LX/TILEX > ELX || LY/TILEY > ELY)
1050     {
1051       /* skipping scan positions to the right and down skips one scan
1052          position too much, because this is only the top left scan position
1053          of totally four scan positions (plus one to the right, one to the
1054          bottom and one to the bottom right) */
1055
1056       LX -= XS;
1057       LY -= YS;
1058     }
1059
1060     return FALSE;
1061   }
1062
1063 #if 0
1064   printf("HitElement (2): element == %d\n", element);
1065 #endif
1066
1067   if (LX + 5 * XS < 0 ||
1068       LY + 5 * YS < 0)
1069   {
1070     LX += 2 * XS;
1071     LY += 2 * YS;
1072
1073     return FALSE;
1074   }
1075
1076 #if 0
1077   printf("HitElement (3): element == %d\n", element);
1078 #endif
1079
1080   if (IS_POLAR(element) &&
1081       ((element - EL_POLAR_START) % 2 ||
1082        (element - EL_POLAR_START) / 2 != laser.current_angle % 8))
1083   {
1084     PlaySoundStereo(SND_KINK, ST(ELX));
1085
1086     laser.num_damages--;
1087
1088     return TRUE;
1089   }
1090
1091   if (IS_POLAR_CROSS(element) &&
1092       (element - EL_POLAR_CROSS_START) != laser.current_angle % 4)
1093   {
1094     PlaySoundStereo(SND_KINK, ST(ELX));
1095
1096     laser.num_damages--;
1097
1098     return TRUE;
1099   }
1100
1101   if (!IS_BEAMER(element) &&
1102       !IS_FIBRE_OPTIC(element) &&
1103       !IS_GRID_WOOD(element) &&
1104       element != EL_FUEL_EMPTY)
1105   {
1106 #if 0
1107     if ((ELX * TILEX + 14 - LX) * YS == (ELY * TILEY + 14 - LY) * XS)
1108       printf("EXACT MATCH @ (%d, %d)\n", ELX, ELY);
1109     else
1110       printf("FUZZY MATCH @ (%d, %d)\n", ELX, ELY);
1111 #endif
1112
1113     LX = ELX * TILEX + 14;
1114     LY = ELY * TILEY + 14;
1115
1116     AddLaserEdge(LX, LY);
1117   }
1118
1119   if (IS_MIRROR(element) ||
1120       IS_MIRROR_FIXED(element) ||
1121       IS_POLAR(element) ||
1122       IS_POLAR_CROSS(element) ||
1123       IS_DF_MIRROR(element) ||
1124       IS_DF_MIRROR_AUTO(element) ||
1125       element == EL_PRISM ||
1126       element == EL_REFRACTOR)
1127   {
1128     int current_angle = laser.current_angle;
1129     int step_size;
1130
1131     laser.num_damages--;
1132
1133     AddDamagedField(ELX, ELY);
1134
1135     laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1136
1137     if (!Hit[ELX][ELY])
1138       Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1139
1140     if (IS_MIRROR(element) ||
1141         IS_MIRROR_FIXED(element) ||
1142         IS_DF_MIRROR(element) ||
1143         IS_DF_MIRROR_AUTO(element))
1144       laser.current_angle = get_mirrored_angle(laser.current_angle,
1145                                                get_element_angle(element));
1146
1147     if (element == EL_PRISM || element == EL_REFRACTOR)
1148       laser.current_angle = RND(16);
1149
1150     XS = 2 * Step[laser.current_angle].x;
1151     YS = 2 * Step[laser.current_angle].y;
1152
1153     if (!IS_22_5_ANGLE(laser.current_angle))    /* 90° or 45° angle */
1154       step_size = 8;
1155     else
1156       step_size = 4;
1157
1158     LX += step_size * XS;
1159     LY += step_size * YS;
1160
1161 #if 0
1162     /* draw sparkles on mirror */
1163     if ((IS_MIRROR(element) || IS_MIRROR_FIXED(element)) &&
1164         current_angle != laser.current_angle)
1165     {
1166       MoveSprite(vp, &Pfeil[2], 4 + 16 * ELX, 5 + 16 * ELY + 1);
1167     }
1168 #endif
1169
1170     if ((!IS_POLAR(element) && !IS_POLAR_CROSS(element)) &&
1171         current_angle != laser.current_angle)
1172       PlaySoundStereo(SND_LASER, ST(ELX));
1173
1174     laser.overloaded =
1175       (get_opposite_angle(laser.current_angle) ==
1176        laser.damage[laser.num_damages - 1].angle ? TRUE : FALSE);
1177
1178     return (laser.overloaded ? TRUE : FALSE);
1179   }
1180
1181   if (element == EL_FUEL_FULL)
1182   {
1183     laser.stops_inside_element = TRUE;
1184
1185     return TRUE;
1186   }
1187
1188   if (element == EL_BOMB || element == EL_MINE)
1189   {
1190     PlaySoundStereo(SND_KINK, ST(ELX));
1191
1192     if (element == EL_MINE)
1193       laser.overloaded = TRUE;
1194   }
1195
1196   if (element == EL_KETTLE ||
1197       element == EL_CELL ||
1198       element == EL_KEY ||
1199       element == EL_LIGHTBALL ||
1200       element == EL_PACMAN ||
1201       IS_PACMAN(element))
1202   {
1203     if (!IS_PACMAN(element))
1204       Bang_MM(ELX, ELY);
1205
1206     if (element == EL_PACMAN)
1207       Bang_MM(ELX, ELY);
1208
1209     if (element == EL_KETTLE || element == EL_CELL)
1210     {
1211       if (game_mm.kettles_still_needed > 0)
1212         game_mm.kettles_still_needed--;
1213
1214       RaiseScore_MM(10);
1215
1216       if (game_mm.kettles_still_needed == 0)
1217       {
1218         int x, y;
1219         static int xy[4][2] =
1220         {
1221           { +1,  0 },
1222           {  0, -1 },
1223           { -1,  0 },
1224           {  0, +1 }
1225         };
1226
1227         PlaySoundStereo(SND_KLING, ST(ELX));
1228
1229         for (y = 0; y < lev_fieldy; y++)
1230         {
1231           for (x = 0; x < lev_fieldx; x++)
1232           {
1233             /* initiate opening animation of exit door */
1234             if (Feld[x][y] == EL_EXIT_CLOSED)
1235               Feld[x][y] = EL_EXIT_OPENING;
1236
1237             /* remove field that blocks receiver */
1238             if (IS_RECEIVER(Feld[x][y]))
1239             {
1240               int phase = Feld[x][y] - EL_RECEIVER_START;
1241               int blocking_x, blocking_y;
1242
1243               blocking_x = x + xy[phase][0];
1244               blocking_y = y + xy[phase][1];
1245
1246               if (IN_LEV_FIELD(blocking_x, blocking_y))
1247               {
1248                 Feld[blocking_x][blocking_y] = EL_EMPTY;
1249
1250                 DrawField_MM(blocking_x, blocking_y);
1251               }
1252             }
1253           }
1254         }
1255
1256         DrawLaser(0, DL_LASER_ENABLED);
1257       }
1258     }
1259     else if (element == EL_KEY)
1260     {
1261       game_mm.num_keys++;
1262     }
1263     else if (element == EL_LIGHTBALL)
1264     {
1265       RaiseScore_MM(10);
1266     }
1267     else if (IS_PACMAN(element))
1268     {
1269       DeletePacMan(ELX, ELY);
1270       RaiseScore_MM(50);
1271     }
1272
1273     return FALSE;
1274   }
1275
1276   if (element == EL_LIGHTBULB_OFF || element == EL_LIGHTBULB_ON)
1277   {
1278     PlaySoundStereo(SND_KINK, ST(ELX));
1279
1280     DrawLaser(0, DL_LASER_ENABLED);
1281
1282     if (Feld[ELX][ELY] == EL_LIGHTBULB_OFF)
1283     {
1284       Feld[ELX][ELY] = EL_LIGHTBULB_ON;
1285       game_mm.lights_still_needed--;
1286     }
1287     else
1288     {
1289       Feld[ELX][ELY] = EL_LIGHTBULB_OFF;
1290       game_mm.lights_still_needed++;
1291     }
1292
1293     DrawField_MM(ELX, ELY);
1294     DrawLaser(0, DL_LASER_ENABLED);
1295
1296     /*
1297     BackToFront();
1298     */
1299     laser.stops_inside_element = TRUE;
1300
1301     return TRUE;
1302   }
1303
1304 #if 0
1305   printf("HitElement (4): element == %d\n", element);
1306 #endif
1307
1308   if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1309       laser.num_beamers < MAX_NUM_BEAMERS &&
1310       laser.beamer[BEAMER_NR(element)][1].num)
1311   {
1312     int beamer_angle = get_element_angle(element);
1313     int beamer_nr = BEAMER_NR(element);
1314     int step_size;
1315
1316 #if 0
1317   printf("HitElement (BEAMER): element == %d\n", element);
1318 #endif
1319
1320     laser.num_damages--;
1321
1322     if (IS_FIBRE_OPTIC(element) ||
1323         laser.current_angle == get_opposite_angle(beamer_angle))
1324     {
1325       int pos;
1326
1327       LX = ELX * TILEX + 14;
1328       LY = ELY * TILEY + 14;
1329
1330       AddLaserEdge(LX, LY);
1331       AddDamagedField(ELX, ELY);
1332
1333       laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1334
1335       if (!Hit[ELX][ELY])
1336         Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1337
1338       pos = (ELX == laser.beamer[beamer_nr][0].x &&
1339              ELY == laser.beamer[beamer_nr][0].y ? 1 : 0);
1340       ELX = laser.beamer[beamer_nr][pos].x;
1341       ELY = laser.beamer[beamer_nr][pos].y;
1342       LX = ELX * TILEX + 14;
1343       LY = ELY * TILEY + 14;
1344
1345       if (IS_BEAMER(element))
1346       {
1347         laser.current_angle = get_element_angle(Feld[ELX][ELY]);
1348         XS = 2 * Step[laser.current_angle].x;
1349         YS = 2 * Step[laser.current_angle].y;
1350       }
1351
1352       laser.beamer_edge[laser.num_beamers] = laser.num_edges;
1353
1354       AddLaserEdge(LX, LY);
1355       AddDamagedField(ELX, ELY);
1356
1357       laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1358
1359       if (!Hit[ELX][ELY])
1360         Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1361
1362       if (laser.current_angle == (laser.current_angle >> 1) << 1)
1363         step_size = 8;
1364       else
1365         step_size = 4;
1366
1367       LX += step_size * XS;
1368       LY += step_size * YS;
1369
1370       laser.num_beamers++;
1371
1372       return FALSE;
1373     }
1374   }
1375
1376   return TRUE;
1377 }
1378
1379 boolean HitOnlyAnEdge(int element, int hit_mask)
1380 {
1381   /* check if the laser hit only the edge of an element and, if so, go on */
1382
1383 #if 0
1384   printf("LX, LY, hit_mask == %d, %d, %d\n", LX, LY, hit_mask);
1385 #endif
1386
1387   if ((hit_mask == HIT_MASK_TOPLEFT ||
1388        hit_mask == HIT_MASK_TOPRIGHT ||
1389        hit_mask == HIT_MASK_BOTTOMLEFT ||
1390        hit_mask == HIT_MASK_BOTTOMRIGHT) &&
1391       laser.current_angle % 4)                  /* angle is not 90° */
1392   {
1393     int dx, dy;
1394
1395     if (hit_mask == HIT_MASK_TOPLEFT)
1396     {
1397       dx = -1;
1398       dy = -1;
1399     }
1400     else if (hit_mask == HIT_MASK_TOPRIGHT)
1401     {
1402       dx = +1;
1403       dy = -1;
1404     }
1405     else if (hit_mask == HIT_MASK_BOTTOMLEFT)
1406     {
1407       dx = -1;
1408       dy = +1;
1409     }
1410     else /* (hit_mask == HIT_MASK_BOTTOMRIGHT) */
1411     {
1412       dx = +1;
1413       dy = +1;
1414     }
1415
1416     AddDamagedField((LX + 2 * dx) / TILEX, (LY + 2 * dy) / TILEY);
1417
1418     LX += XS;
1419     LY += YS;
1420
1421 #if 0
1422     printf("[HitOnlyAnEdge() == TRUE]\n");
1423 #endif
1424
1425     return TRUE;
1426   }
1427
1428 #if 0
1429     printf("[HitOnlyAnEdge() == FALSE]\n");
1430 #endif
1431
1432   return FALSE;
1433 }
1434
1435 boolean HitPolarizer(int element, int hit_mask)
1436 {
1437   if (HitOnlyAnEdge(element, hit_mask))
1438     return FALSE;
1439
1440   if (IS_DF_GRID(element))
1441   {
1442     int grid_angle = get_element_angle(element);
1443
1444 #if 0
1445     printf("HitPolarizer: angle: grid == %d, laser == %d\n",
1446            grid_angle, laser.current_angle);
1447 #endif
1448
1449     AddLaserEdge(LX, LY);
1450     AddDamagedField(ELX, ELY);
1451
1452     if (!Hit[ELX][ELY])
1453       Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1454
1455     if (laser.current_angle == grid_angle ||
1456         laser.current_angle == get_opposite_angle(grid_angle))
1457     {
1458       /* skip the whole element before continuing the scan */
1459       do
1460       {
1461         LX += XS;
1462         LY += YS;
1463       }
1464       while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1465
1466       if (LX/TILEX > ELX || LY/TILEY > ELY)
1467       {
1468         /* skipping scan positions to the right and down skips one scan
1469            position too much, because this is only the top left scan position
1470            of totally four scan positions (plus one to the right, one to the
1471            bottom and one to the bottom right) */
1472
1473         LX -= XS;
1474         LY -= YS;
1475       }
1476
1477       AddLaserEdge(LX, LY);
1478
1479       LX += XS;
1480       LY += YS;
1481
1482 #if 0
1483       printf("HitPolarizer: LX, LY == %d, %d [%d, %d] [%d, %d]\n",
1484              LX, LY,
1485              LX / TILEX, LY / TILEY,
1486              LX % TILEX, LY % TILEY);
1487 #endif
1488
1489       return FALSE;
1490     }
1491     else if (IS_GRID_STEEL_FIXED(element) || IS_GRID_STEEL_AUTO(element))
1492     {
1493       return HitReflectingWalls(element, hit_mask);
1494     }
1495     else
1496     {
1497       return HitAbsorbingWalls(element, hit_mask);
1498     }
1499   }
1500   else if (IS_GRID_STEEL(element))
1501   {
1502     return HitReflectingWalls(element, hit_mask);
1503   }
1504   else  /* IS_GRID_WOOD */
1505   {
1506     return HitAbsorbingWalls(element, hit_mask);
1507   }
1508
1509   return TRUE;
1510 }
1511
1512 boolean HitBlock(int element, int hit_mask)
1513 {
1514   boolean check = FALSE;
1515
1516   if ((element == EL_GATE_STONE || element == EL_GATE_WOOD) &&
1517       game_mm.num_keys == 0)
1518     check = TRUE;
1519
1520   if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1521   {
1522     int i, x, y;
1523     int ex = ELX * TILEX + 14;
1524     int ey = ELY * TILEY + 14;
1525
1526     check = TRUE;
1527
1528     for (i = 1; i < 32; i++)
1529     {
1530       x = LX + i * XS;
1531       y = LY + i * YS;
1532
1533       if ((x == ex || x == ex + 1) && (y == ey || y == ey + 1))
1534         check = FALSE;
1535     }
1536   }
1537
1538   if (check && (element == EL_BLOCK_WOOD || element == EL_GATE_WOOD))
1539     return HitAbsorbingWalls(element, hit_mask);
1540
1541   if (check)
1542   {
1543     AddLaserEdge(LX - XS, LY - YS);
1544     AddDamagedField(ELX, ELY);
1545
1546     if (!Box[ELX][ELY])
1547       Box[ELX][ELY] = laser.num_edges;
1548
1549     return HitReflectingWalls(element, hit_mask);
1550   }
1551
1552   if (element == EL_GATE_STONE || element == EL_GATE_WOOD)
1553   {
1554     int xs = XS / 2, ys = YS / 2;
1555     int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1556     int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1557
1558     if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1559         (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1560     {
1561       laser.overloaded = (element == EL_GATE_STONE);
1562
1563       return TRUE;
1564     }
1565
1566     if (ABS(xs) == 1 && ABS(ys) == 1 &&
1567         (hit_mask == HIT_MASK_TOP ||
1568          hit_mask == HIT_MASK_LEFT ||
1569          hit_mask == HIT_MASK_RIGHT ||
1570          hit_mask == HIT_MASK_BOTTOM))
1571       AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1572                                   hit_mask == HIT_MASK_BOTTOM),
1573                       ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1574                                   hit_mask == HIT_MASK_RIGHT));
1575     AddLaserEdge(LX, LY);
1576
1577     Bang_MM(ELX, ELY);
1578
1579     game_mm.num_keys--;
1580
1581     if (element == EL_GATE_STONE && Box[ELX][ELY])
1582     {
1583       DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
1584       /*
1585       BackToFront();
1586       */
1587       ScanLaser();
1588
1589       return TRUE;
1590     }
1591
1592     return FALSE;
1593   }
1594
1595   if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1596   {
1597     int xs = XS / 2, ys = YS / 2;
1598     int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1599     int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1600
1601     if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1602         (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1603     {
1604       laser.overloaded = (element == EL_BLOCK_STONE);
1605
1606       return TRUE;
1607     }
1608
1609     if (ABS(xs) == 1 && ABS(ys) == 1 &&
1610         (hit_mask == HIT_MASK_TOP ||
1611          hit_mask == HIT_MASK_LEFT ||
1612          hit_mask == HIT_MASK_RIGHT ||
1613          hit_mask == HIT_MASK_BOTTOM))
1614       AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1615                                   hit_mask == HIT_MASK_BOTTOM),
1616                       ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1617                                   hit_mask == HIT_MASK_RIGHT));
1618     AddDamagedField(ELX, ELY);
1619
1620     LX = ELX * TILEX + 14;
1621     LY = ELY * TILEY + 14;
1622
1623     AddLaserEdge(LX, LY);
1624
1625     laser.stops_inside_element = TRUE;
1626
1627     return TRUE;
1628   }
1629
1630   return TRUE;
1631 }
1632
1633 boolean HitLaserSource(int element, int hit_mask)
1634 {
1635    if (HitOnlyAnEdge(element, hit_mask))
1636      return FALSE;
1637
1638    PlaySoundStereo(SND_AUTSCH, ST(ELX));
1639    laser.overloaded = TRUE;
1640
1641    return TRUE;
1642 }
1643
1644 boolean HitLaserDestination(int element, int hit_mask)
1645 {
1646   if (HitOnlyAnEdge(element, hit_mask))
1647     return FALSE;
1648
1649   if (element != EL_EXIT_OPEN &&
1650       !(IS_RECEIVER(element) &&
1651         game_mm.kettles_still_needed == 0 &&
1652         laser.current_angle == get_opposite_angle(get_element_angle(element))))
1653   {
1654     PlaySoundStereo(SND_HOLZ, ST(ELX));
1655     return TRUE;
1656   }
1657
1658   if (IS_RECEIVER(element) ||
1659       (IS_22_5_ANGLE(laser.current_angle) &&
1660        (ELX != (LX + 6 * XS) / TILEX ||
1661         ELY != (LY + 6 * YS) / TILEY ||
1662         LX + 6 * XS < 0 ||
1663         LY + 6 * YS < 0)))
1664   {
1665     LX -= XS;
1666     LY -= YS;
1667   }
1668   else
1669   {
1670     LX = ELX * TILEX + 14;
1671     LY = ELY * TILEY + 14;
1672
1673     laser.stops_inside_element = TRUE;
1674   }
1675
1676   AddLaserEdge(LX, LY);
1677   AddDamagedField(ELX, ELY);
1678
1679   if (game_mm.lights_still_needed == 0)
1680     game_mm.level_solved = TRUE;
1681
1682   return TRUE;
1683 }
1684
1685 boolean HitReflectingWalls(int element, int hit_mask)
1686 {
1687   /* check if laser hits side of a wall with an angle that is not 90° */
1688   if (!IS_90_ANGLE(laser.current_angle) && (hit_mask == HIT_MASK_TOP ||
1689                                             hit_mask == HIT_MASK_LEFT ||
1690                                             hit_mask == HIT_MASK_RIGHT ||
1691                                             hit_mask == HIT_MASK_BOTTOM))
1692   {
1693     PlaySoundStereo(SND_HUI, ST(ELX));
1694
1695     LX -= XS;
1696     LY -= YS;
1697
1698     if (!IS_DF_GRID(element))
1699       AddLaserEdge(LX, LY);
1700
1701     /* check if laser hits wall with an angle of 45° */
1702     if (!IS_22_5_ANGLE(laser.current_angle))
1703     {
1704       if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1705       {
1706         LX += 2 * XS;
1707         laser.current_angle = get_mirrored_angle(laser.current_angle,
1708                                                  ANG_MIRROR_0);
1709       }
1710       else      /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1711       {
1712         LY += 2 * YS;
1713         laser.current_angle = get_mirrored_angle(laser.current_angle,
1714                                                  ANG_MIRROR_90);
1715       }
1716
1717       AddLaserEdge(LX, LY);
1718
1719       XS = 2 * Step[laser.current_angle].x;
1720       YS = 2 * Step[laser.current_angle].y;
1721
1722       return FALSE;
1723     }
1724     else if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1725     {
1726       laser.current_angle = get_mirrored_angle(laser.current_angle,
1727                                                ANG_MIRROR_0);
1728       if (ABS(XS) == 4)
1729       {
1730         LX += 2 * XS;
1731         if (!IS_DF_GRID(element))
1732           AddLaserEdge(LX, LY);
1733       }
1734       else
1735       {
1736         LX += XS;
1737         if (!IS_DF_GRID(element))
1738           AddLaserEdge(LX, LY + YS / 2);
1739
1740         LX += XS;
1741         if (!IS_DF_GRID(element))
1742           AddLaserEdge(LX, LY);
1743       }
1744
1745       YS = 2 * Step[laser.current_angle].y;
1746
1747       return FALSE;
1748     }
1749     else        /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1750     {
1751       laser.current_angle = get_mirrored_angle(laser.current_angle,
1752                                                ANG_MIRROR_90);
1753       if (ABS(YS) == 4)
1754       {
1755         LY += 2 * YS;
1756         if (!IS_DF_GRID(element))
1757           AddLaserEdge(LX, LY);
1758       }
1759       else
1760       {
1761         LY += YS;
1762         if (!IS_DF_GRID(element))
1763           AddLaserEdge(LX + XS / 2, LY);
1764
1765         LY += YS;
1766         if (!IS_DF_GRID(element))
1767           AddLaserEdge(LX, LY);
1768       }
1769
1770       XS = 2 * Step[laser.current_angle].x;
1771
1772       return FALSE;
1773     }
1774   }
1775
1776   /* reflection at the edge of reflecting DF style wall */
1777   if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1778   {
1779     if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1780          hit_mask == HIT_MASK_TOPRIGHT) ||
1781         ((laser.current_angle == 5 || laser.current_angle == 7) &&
1782          hit_mask == HIT_MASK_TOPLEFT) ||
1783         ((laser.current_angle == 9 || laser.current_angle == 11) &&
1784          hit_mask == HIT_MASK_BOTTOMLEFT) ||
1785         ((laser.current_angle == 13 || laser.current_angle == 15) &&
1786          hit_mask == HIT_MASK_BOTTOMRIGHT))
1787     {
1788       int mirror_angle =
1789         (hit_mask == HIT_MASK_TOPRIGHT || hit_mask == HIT_MASK_BOTTOMLEFT ?
1790          ANG_MIRROR_135 : ANG_MIRROR_45);
1791
1792       PlaySoundStereo(SND_HUI, ST(ELX));
1793
1794       AddDamagedField(ELX, ELY);
1795       AddLaserEdge(LX, LY);
1796
1797       laser.current_angle = get_mirrored_angle(laser.current_angle,
1798                                                mirror_angle);
1799       XS = 8 / -XS;
1800       YS = 8 / -YS;
1801
1802       LX += XS;
1803       LY += YS;
1804
1805       AddLaserEdge(LX, LY);
1806
1807       return FALSE;
1808     }
1809   }
1810
1811   /* reflection inside an edge of reflecting DF style wall */
1812   if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1813   {
1814     if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1815          hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT)) ||
1816         ((laser.current_angle == 5 || laser.current_angle == 7) &&
1817          hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMRIGHT)) ||
1818         ((laser.current_angle == 9 || laser.current_angle == 11) &&
1819          hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT)) ||
1820         ((laser.current_angle == 13 || laser.current_angle == 15) &&
1821          hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPLEFT)))
1822     {
1823       int mirror_angle =
1824         (hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT) ||
1825          hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT) ?
1826          ANG_MIRROR_135 : ANG_MIRROR_45);
1827
1828       PlaySoundStereo(SND_HUI, ST(ELX));
1829
1830       /*
1831       AddDamagedField(ELX, ELY);
1832       */
1833
1834       AddLaserEdge(LX - XS, LY - YS);
1835       AddLaserEdge(LX - XS + (ABS(XS) == 4 ? XS/2 : 0),
1836                    LY - YS + (ABS(YS) == 4 ? YS/2 : 0));
1837
1838       laser.current_angle = get_mirrored_angle(laser.current_angle,
1839                                                mirror_angle);
1840       XS = 8 / -XS;
1841       YS = 8 / -YS;
1842
1843       LX += XS;
1844       LY += YS;
1845
1846       AddLaserEdge(LX, LY);
1847
1848       return FALSE;
1849     }
1850   }
1851
1852   /* check if laser hits DF style wall with an angle of 90° */
1853   if (IS_DF_WALL(element) && IS_90_ANGLE(laser.current_angle))
1854   {
1855     if ((IS_HORIZ_ANGLE(laser.current_angle) &&
1856          (!(hit_mask & HIT_MASK_TOP) || !(hit_mask & HIT_MASK_BOTTOM))) ||
1857         (IS_VERT_ANGLE(laser.current_angle) &&
1858          (!(hit_mask & HIT_MASK_LEFT) || !(hit_mask & HIT_MASK_RIGHT))))
1859     {
1860       static int last_LX = 0, last_LY = 0, last_hit_mask = 0;
1861
1862       /* laser at last step touched nothing or the same side of the wall */
1863       if (LX != last_LX || LY != last_LY || hit_mask == last_hit_mask)
1864       {
1865         AddDamagedField(ELX, ELY);
1866
1867         LX += 8 * XS;
1868         LY += 8 * YS;
1869
1870         last_LX = LX;
1871         last_LY = LY;
1872         last_hit_mask = hit_mask;
1873
1874         return FALSE;
1875       }
1876     }
1877   }
1878
1879   if (!HitOnlyAnEdge(element, hit_mask))
1880   {
1881     laser.overloaded = TRUE;
1882
1883     return TRUE;
1884   }
1885
1886   return FALSE;
1887 }
1888
1889 boolean HitAbsorbingWalls(int element, int hit_mask)
1890 {
1891   if (HitOnlyAnEdge(element, hit_mask))
1892     return FALSE;
1893
1894   if (ABS(XS) == 4 &&
1895       (hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT))
1896   {
1897     AddLaserEdge(LX - XS, LY - YS);
1898
1899     LX = LX + XS / 2;
1900     LY = LY + YS;
1901   }
1902
1903   if (ABS(YS) == 4 &&
1904       (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM))
1905   {
1906     AddLaserEdge(LX - XS, LY - YS);
1907
1908     LX = LX + XS;
1909     LY = LY + YS / 2;
1910   }
1911
1912   if (IS_WALL_WOOD(element) ||
1913       IS_DF_WALL_WOOD(element) ||
1914       IS_GRID_WOOD(element) ||
1915       IS_GRID_WOOD_FIXED(element) ||
1916       IS_GRID_WOOD_AUTO(element) ||
1917       element == EL_FUSE_ON ||
1918       element == EL_BLOCK_WOOD ||
1919       element == EL_GATE_WOOD)
1920   {
1921     PlaySoundStereo(SND_HOLZ, ST(ELX));
1922
1923     return TRUE;
1924   }
1925
1926   if (IS_WALL_ICE(element))
1927   {
1928     int mask;
1929
1930     mask = (LX + XS) / MINI_TILEX - ELX * 2 + 1;    /* Quadrant (horizontal) */
1931     mask <<= (((LY + YS) / MINI_TILEY - ELY * 2) > 0) * 2;  /* || (vertical) */
1932
1933     /* check if laser hits wall with an angle of 90° */
1934     if (IS_90_ANGLE(laser.current_angle))
1935       mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1936
1937     if (mask == 1 || mask == 2 || mask == 4 || mask == 8)
1938     {
1939       int i;
1940
1941       for (i = 0; i < 4; i++)
1942       {
1943         if (mask == (1 << i) && (XS > 0) == (i % 2) && (YS > 0) == (i / 2))
1944           mask = 15 - (8 >> i);
1945         else if (ABS(XS) == 4 &&
1946                  mask == (1 << i) &&
1947                  (XS > 0) == (i % 2) &&
1948                  (YS < 0) == (i / 2))
1949           mask = 3 + (i / 2) * 9;
1950         else if (ABS(YS) == 4 &&
1951                  mask == (1 << i) &&
1952                  (XS < 0) == (i % 2) &&
1953                  (YS > 0) == (i / 2))
1954           mask = 5 + (i % 2) * 5;
1955       }
1956     }
1957
1958     laser.wall_mask = mask;
1959   }
1960   else if (IS_WALL_AMOEBA(element))
1961   {
1962     int elx = (LX - 2 * XS) / TILEX;
1963     int ely = (LY - 2 * YS) / TILEY;
1964     int element2 = Feld[elx][ely];
1965     int mask;
1966
1967     if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element2))
1968     {
1969       laser.dest_element = EL_EMPTY;
1970
1971       return TRUE;
1972     }
1973
1974     ELX = elx;
1975     ELY = ely;
1976
1977     mask = (LX - 2 * XS) / 16 - ELX * 2 + 1;
1978     mask <<= ((LY - 2 * YS) / 16 - ELY * 2) * 2;
1979
1980     if (IS_90_ANGLE(laser.current_angle))
1981       mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1982
1983     laser.dest_element = element2 | EL_WALL_AMOEBA;
1984
1985     laser.wall_mask = mask;
1986   }
1987
1988   return TRUE;
1989 }
1990
1991 void OpenExit(int x, int y)
1992 {
1993   int delay = 6;
1994
1995   if (!MovDelay[x][y])          /* next animation frame */
1996     MovDelay[x][y] = 4 * delay;
1997
1998   if (MovDelay[x][y])           /* wait some time before next frame */
1999   {
2000     int phase;
2001
2002     MovDelay[x][y]--;
2003     phase = MovDelay[x][y] / delay;
2004
2005     if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2006       DrawGraphicAnimation_MM(x, y, IMG_MM_EXIT_OPENING, 3 - phase);
2007
2008     if (!MovDelay[x][y])
2009     {
2010       Feld[x][y] = EL_EXIT_OPEN;
2011       DrawField_MM(x, y);
2012     }
2013   }
2014 }
2015
2016 void OpenSurpriseBall(int x, int y)
2017 {
2018   int delay = 2;
2019
2020   if (!MovDelay[x][y])          /* next animation frame */
2021     MovDelay[x][y] = 50 * delay;
2022
2023   if (MovDelay[x][y])           /* wait some time before next frame */
2024   {
2025     MovDelay[x][y]--;
2026
2027     if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2028     {
2029       Bitmap *bitmap;
2030       int graphic = el2gfx(Store[x][y]);
2031       int gx, gy;
2032       int dx = RND(26), dy = RND(26);
2033
2034       getGraphicSource(graphic, 0, &bitmap, &gx, &gy);
2035
2036       BlitBitmap(bitmap, drawto, gx + dx, gy + dy, 6, 6,
2037                  SX + x * TILEX + dx, SY + y * TILEY + dy);
2038
2039       MarkTileDirty(x, y);
2040     }
2041
2042     if (!MovDelay[x][y])
2043     {
2044       Feld[x][y] = Store[x][y];
2045       Store[x][y] = 0;
2046       DrawField_MM(x, y);
2047
2048       ScanLaser();
2049     }
2050   }
2051 }
2052
2053 void MeltIce(int x, int y)
2054 {
2055   int frames = 5;
2056   int delay = 5;
2057
2058   if (!MovDelay[x][y])          /* next animation frame */
2059     MovDelay[x][y] = frames * delay;
2060
2061   if (MovDelay[x][y])           /* wait some time before next frame */
2062   {
2063     int phase;
2064     int wall_mask = Store2[x][y];
2065     int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_ICE;
2066
2067     MovDelay[x][y]--;
2068     phase = frames - MovDelay[x][y] / delay - 1;
2069
2070     if (!MovDelay[x][y])
2071     {
2072       int i;
2073
2074       Feld[x][y] = real_element & (wall_mask ^ 0xFF);
2075       Store[x][y] = Store2[x][y] = 0;
2076
2077       DrawWalls_MM(x, y, Feld[x][y]);
2078
2079       if (Feld[x][y] == EL_WALL_ICE)
2080         Feld[x][y] = EL_EMPTY;
2081
2082       for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
2083         if (laser.damage[i].is_mirror)
2084           break;
2085
2086       if (i > 0)
2087         DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
2088       else
2089         DrawLaser(0, DL_LASER_DISABLED);
2090
2091       ScanLaser();
2092     }
2093     else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2094     {
2095       DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2096
2097       laser.redraw = TRUE;
2098     }
2099   }
2100 }
2101
2102 void GrowAmoeba(int x, int y)
2103 {
2104   int frames = 5;
2105   int delay = 1;
2106
2107   if (!MovDelay[x][y])          /* next animation frame */
2108     MovDelay[x][y] = frames * delay;
2109
2110   if (MovDelay[x][y])           /* wait some time before next frame */
2111   {
2112     int phase;
2113     int wall_mask = Store2[x][y];
2114     int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_AMOEBA;
2115
2116     MovDelay[x][y]--;
2117     phase = MovDelay[x][y] / delay;
2118
2119     if (!MovDelay[x][y])
2120     {
2121       Feld[x][y] = real_element;
2122       Store[x][y] = Store2[x][y] = 0;
2123
2124       DrawWalls_MM(x, y, Feld[x][y]);
2125       DrawLaser(0, DL_LASER_ENABLED);
2126     }
2127     else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2128     {
2129       DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2130     }
2131   }
2132 }
2133
2134 static void Explode_MM(int x, int y, int phase, int mode)
2135 {
2136   int num_phase = 9, delay = 2;
2137   int last_phase = num_phase * delay;
2138   int half_phase = (num_phase / 2) * delay;
2139
2140   laser.redraw = TRUE;
2141
2142   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
2143   {
2144     int center_element = Feld[x][y];
2145
2146     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2147     {
2148       /* put moving element to center field (and let it explode there) */
2149       center_element = MovingOrBlocked2Element_MM(x, y);
2150       RemoveMovingField_MM(x, y);
2151
2152       Feld[x][y] = center_element;
2153     }
2154
2155     if (center_element == EL_BOMB || IS_MCDUFFIN(center_element))
2156       Store[x][y] = center_element;
2157     else
2158       Store[x][y] = EL_EMPTY;
2159
2160     Store2[x][y] = mode;
2161     Feld[x][y] = EL_EXPLODING_OPAQUE;
2162     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2163     Frame[x][y] = 1;
2164
2165     return;
2166   }
2167
2168   Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
2169
2170   if (phase == half_phase)
2171   {
2172     Feld[x][y] = EL_EXPLODING_TRANSP;
2173
2174     if (x == ELX && y == ELY)
2175       ScanLaser();
2176   }
2177
2178   if (phase == last_phase)
2179   {
2180     if (Store[x][y] == EL_BOMB)
2181     {
2182       laser.num_damages--;
2183       DrawLaser(0, DL_LASER_DISABLED);
2184       laser.num_edges = 0;
2185
2186       Bang_MM(laser.start_edge.x, laser.start_edge.y);
2187       Store[x][y] = EL_EMPTY;
2188     }
2189     else if (IS_MCDUFFIN(Store[x][y]))
2190     {
2191       game_mm.game_over = TRUE;
2192       game_mm.game_over_cause = GAME_OVER_BOMB;
2193       Store[x][y] = EL_EMPTY;
2194     }
2195
2196     Feld[x][y] = Store[x][y];
2197     Store[x][y] = Store2[x][y] = 0;
2198     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2199
2200     InitField(x, y, FALSE);
2201     DrawField_MM(x, y);
2202   }
2203   else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2204   {
2205     int graphic = IMG_MM_DEFAULT_EXPLODING;
2206     int graphic_phase = (phase / delay - 1);
2207     Bitmap *bitmap;
2208     int src_x, src_y;
2209
2210     if (Store2[x][y] == EX_KETTLE)
2211     {
2212       if (graphic_phase < 3)
2213       {
2214         graphic = IMG_MM_KETTLE_EXPLODING;
2215       }
2216       else if (graphic_phase < 5)
2217       {
2218         graphic_phase += 3;
2219       }
2220       else
2221       {
2222         graphic = IMG_EMPTY;
2223         graphic_phase = 0;
2224       }
2225     }
2226     else if (Store2[x][y] == EX_SHORT)
2227     {
2228       if (graphic_phase < 4)
2229       {
2230         graphic_phase += 4;
2231       }
2232       else
2233       {
2234         graphic = IMG_EMPTY;
2235         graphic_phase = 0;
2236       }
2237     }
2238
2239     getGraphicSource(graphic, graphic_phase, &bitmap, &src_x, &src_y);
2240
2241     BlitBitmap(bitmap, drawto_field, src_x, src_y, TILEX, TILEY,
2242                FX + x * TILEX, FY + y * TILEY);
2243
2244     MarkTileDirty(x, y);
2245   }
2246 }
2247
2248 static void Bang_MM(int x, int y)
2249 {
2250   int element = Feld[x][y];
2251   int mode = EX_NORMAL;
2252
2253 #if 0
2254   DrawLaser(0, DL_LASER_ENABLED);
2255 #endif
2256
2257   switch(element)
2258   {
2259     case EL_KETTLE:
2260       mode = EX_KETTLE;
2261       break;
2262
2263     case EL_GATE_STONE:
2264     case EL_GATE_WOOD:
2265       mode = EX_SHORT;
2266       break;
2267
2268     default:
2269       mode = EX_NORMAL;
2270       break;
2271   }
2272
2273   if (IS_PACMAN(element))
2274     PlaySoundStereo(SND_QUIEK, ST(x));
2275   else if (element == EL_BOMB || IS_MCDUFFIN(element))
2276     PlaySoundStereo(SND_ROAAAR, ST(x));
2277   else if (element == EL_KEY)
2278     PlaySoundStereo(SND_KLING, ST(x));
2279   else
2280     PlaySoundStereo((mode == EX_SHORT ? SND_WHOOSH : SND_KABUMM), ST(x));
2281
2282   Explode_MM(x, y, EX_PHASE_START, mode);
2283 }
2284
2285 void TurnRound(int x, int y)
2286 {
2287   static struct
2288   {
2289     int x, y;
2290   } move_xy[] =
2291   {
2292     { 0, 0 },
2293     {-1, 0 },
2294     {+1, 0 },
2295     { 0, 0 },
2296     { 0, -1 },
2297     { 0, 0 }, { 0, 0 }, { 0, 0 },
2298     { 0, +1 }
2299   };
2300   static struct
2301   {
2302     int left, right, back;
2303   } turn[] =
2304   {
2305     { 0,        0,              0 },
2306     { MV_DOWN,  MV_UP,          MV_RIGHT },
2307     { MV_UP,    MV_DOWN,        MV_LEFT },
2308     { 0,        0,              0 },
2309     { MV_LEFT,  MV_RIGHT,       MV_DOWN },
2310     { 0,0,0 },  { 0,0,0 },      { 0,0,0 },
2311     { MV_RIGHT, MV_LEFT,        MV_UP }
2312   };
2313
2314   int element = Feld[x][y];
2315   int old_move_dir = MovDir[x][y];
2316   int right_dir = turn[old_move_dir].right;
2317   int back_dir = turn[old_move_dir].back;
2318   int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
2319   int right_x = x + right_dx, right_y = y + right_dy;
2320
2321   if (element == EL_PACMAN)
2322   {
2323     boolean can_turn_right = FALSE;
2324
2325     if (IN_LEV_FIELD(right_x, right_y) &&
2326         IS_EATABLE4PACMAN(Feld[right_x][right_y]))
2327       can_turn_right = TRUE;
2328
2329     if (can_turn_right)
2330       MovDir[x][y] = right_dir;
2331     else
2332       MovDir[x][y] = back_dir;
2333
2334     MovDelay[x][y] = 0;
2335   }
2336 }
2337
2338 static void StartMoving_MM(int x, int y)
2339 {
2340   int element = Feld[x][y];
2341
2342   if (Stop[x][y])
2343     return;
2344
2345   if (CAN_MOVE(element))
2346   {
2347     int newx, newy;
2348
2349     if (MovDelay[x][y])         /* wait some time before next movement */
2350     {
2351       MovDelay[x][y]--;
2352
2353       if (MovDelay[x][y])
2354         return;
2355     }
2356
2357     /* now make next step */
2358
2359     Moving2Blocked_MM(x, y, &newx, &newy);      /* get next screen position */
2360
2361     if (element == EL_PACMAN &&
2362         IN_LEV_FIELD(newx, newy) && IS_EATABLE4PACMAN(Feld[newx][newy]) &&
2363         !ObjHit(newx, newy, HIT_POS_CENTER))
2364     {
2365       Store[newx][newy] = Feld[newx][newy];
2366       Feld[newx][newy] = EL_EMPTY;
2367
2368       DrawField_MM(newx, newy);
2369     }
2370     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy) ||
2371              ObjHit(newx, newy, HIT_POS_CENTER))
2372     {
2373       /* object was running against a wall */
2374
2375       TurnRound(x, y);
2376
2377       return;
2378     }
2379
2380     InitMovingField_MM(x, y, MovDir[x][y]);
2381   }
2382
2383   if (MovDir[x][y])
2384     ContinueMoving_MM(x, y);
2385 }
2386
2387 static void ContinueMoving_MM(int x, int y)
2388 {
2389   int element = Feld[x][y];
2390   int direction = MovDir[x][y];
2391   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2392   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
2393   int horiz_move = (dx!=0);
2394   int newx = x + dx, newy = y + dy;
2395   int step = (horiz_move ? dx : dy) * TILEX / 8;
2396
2397   MovPos[x][y] += step;
2398
2399   if (ABS(MovPos[x][y]) >= TILEX)       /* object reached its destination */
2400   {
2401     Feld[x][y] = EL_EMPTY;
2402     Feld[newx][newy] = element;
2403
2404     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2405     MovDelay[newx][newy] = 0;
2406
2407     if (!CAN_MOVE(element))
2408       MovDir[newx][newy] = 0;
2409
2410     DrawField_MM(x, y);
2411     DrawField_MM(newx, newy);
2412
2413     Stop[newx][newy] = TRUE;
2414
2415     if (element == EL_PACMAN)
2416     {
2417       if (Store[newx][newy] == EL_BOMB)
2418         Bang_MM(newx, newy);
2419
2420       if (IS_WALL_AMOEBA(Store[newx][newy]) &&
2421           (LX + 2 * XS) / TILEX == newx &&
2422           (LY + 2 * YS) / TILEY == newy)
2423       {
2424         laser.num_edges--;
2425         ScanLaser();
2426       }
2427     }
2428   }
2429   else                          /* still moving on */
2430   {
2431     DrawField_MM(x, y);
2432   }
2433
2434   laser.redraw = TRUE;
2435 }
2436
2437 void ClickElement(int mx, int my, int button)
2438 {
2439   static unsigned int click_delay = 0;
2440   static int click_delay_value = CLICK_DELAY_SHORT;
2441   static boolean new_button = TRUE;
2442   int element;
2443   int x = (mx - SX) / TILEX, y = (my - SY) / TILEY;
2444
2445   /* do not rotate objects hit by the laser after the game was solved */
2446   if (game_mm.level_solved && Hit[x][y])
2447     return;
2448
2449   if (button == MB_RELEASED)
2450   {
2451     new_button = TRUE;
2452     click_delay_value = CLICK_DELAY_SHORT;
2453
2454     /* release eventually hold auto-rotating mirror */
2455     RotateMirror(x, y, MB_RELEASED);
2456
2457     return;
2458   }
2459
2460   if (!DelayReached(&click_delay, click_delay_value) && !new_button)
2461     return;
2462
2463   if (button == MB_MIDDLEBUTTON)        /* middle button has no function */
2464     return;
2465
2466   if (!IN_PIX_FIELD(mx - SX, my - SY))
2467     return;
2468
2469   if (Feld[x][y] == EL_EMPTY)
2470     return;
2471
2472   element = Feld[x][y];
2473
2474   if (IS_MIRROR(element) ||
2475       IS_BEAMER(element) ||
2476       IS_POLAR(element) ||
2477       IS_POLAR_CROSS(element) ||
2478       IS_DF_MIRROR(element) ||
2479       IS_DF_MIRROR_AUTO(element))
2480   {
2481     RotateMirror(x, y, button);
2482   }
2483   else if (IS_MCDUFFIN(element))
2484   {
2485     if (!laser.fuse_off)
2486     {
2487       DrawLaser(0, DL_LASER_DISABLED);
2488
2489       /*
2490       BackToFront();
2491       */
2492     }
2493
2494     element = get_rotated_element(element, BUTTON_ROTATION(button));
2495     laser.start_angle = get_element_angle(element);
2496
2497     InitLaser();
2498
2499     Feld[x][y] = element;
2500     DrawField_MM(x, y);
2501
2502     /*
2503     BackToFront();
2504     */
2505
2506     if (!laser.fuse_off)
2507       ScanLaser();
2508   }
2509   else if (element == EL_FUSE_ON && laser.fuse_off)
2510   {
2511     if (x != laser.fuse_x || y != laser.fuse_y)
2512       return;
2513
2514     laser.fuse_off = FALSE;
2515     laser.fuse_x = laser.fuse_y = -1;
2516
2517     DrawGraphic_MM(x, y, IMG_MM_FUSE_ACTIVE);
2518     ScanLaser();
2519   }
2520   else if (element == EL_FUSE_ON && !laser.fuse_off && new_button)
2521   {
2522     laser.fuse_off = TRUE;
2523     laser.fuse_x = x;
2524     laser.fuse_y = y;
2525     laser.overloaded = FALSE;
2526
2527     DrawLaser(0, DL_LASER_DISABLED);
2528     DrawGraphic_MM(x, y, IMG_MM_FUSE);
2529   }
2530   else if (element == EL_LIGHTBALL)
2531   {
2532     Bang_MM(x, y);
2533     RaiseScore_MM(10);
2534     DrawLaser(0, DL_LASER_ENABLED);
2535   }
2536
2537   click_delay_value = (new_button ? CLICK_DELAY_LONG : CLICK_DELAY_SHORT);
2538   new_button = FALSE;
2539 }
2540
2541 void RotateMirror(int x, int y, int button)
2542 {
2543   static int hold_x = -1, hold_y = -1;
2544
2545   if (button == MB_RELEASED)
2546   {
2547     /* release eventually hold auto-rotating mirror */
2548     hold_x = -1;
2549     hold_y = -1;
2550
2551     return;
2552   }
2553
2554   if (IS_MIRROR(Feld[x][y]) ||
2555       IS_POLAR_CROSS(Feld[x][y]) ||
2556       IS_POLAR(Feld[x][y]) ||
2557       IS_BEAMER(Feld[x][y]) ||
2558       IS_DF_MIRROR(Feld[x][y]) ||
2559       IS_GRID_STEEL_AUTO(Feld[x][y]) ||
2560       IS_GRID_WOOD_AUTO(Feld[x][y]))
2561   {
2562     Feld[x][y] = get_rotated_element(Feld[x][y], BUTTON_ROTATION(button));
2563   }
2564   else if (IS_DF_MIRROR_AUTO(Feld[x][y]))
2565   {
2566     if (button == MB_LEFTBUTTON)
2567     {
2568       /* left mouse button only for manual adjustment, no auto-rotating;
2569          freeze mirror for until mouse button released */
2570       hold_x = x;
2571       hold_y = y;
2572     }
2573     else if (button == MB_RIGHTBUTTON && (hold_x != x || hold_y != y))
2574     {
2575       Feld[x][y] = get_rotated_element(Feld[x][y], ROTATE_RIGHT);
2576     }
2577   }
2578
2579   if (IS_GRID_STEEL_AUTO(Feld[x][y]) || IS_GRID_WOOD_AUTO(Feld[x][y]))
2580   {
2581     int edge = Hit[x][y];
2582
2583     DrawField_MM(x, y);
2584
2585     if (edge > 0)
2586     {
2587       DrawLaser(edge - 1, DL_LASER_DISABLED);
2588       ScanLaser();
2589     }
2590   }
2591   else if (ObjHit(x, y, HIT_POS_CENTER))
2592   {
2593     int edge = Hit[x][y];
2594
2595     if (edge == 0)
2596     {
2597       Error(ERR_WARN, "RotateMirror: inconsistent field Hit[][]!\n");
2598       edge = 1;
2599     }
2600
2601     DrawLaser(edge - 1, DL_LASER_DISABLED);
2602     ScanLaser();
2603   }
2604   else
2605   {
2606     int check = 1;
2607
2608     if (ObjHit(x, y, HIT_POS_EDGE | HIT_POS_BETWEEN))
2609       check = 2;
2610
2611     DrawField_MM(x, y);
2612
2613     if ((IS_BEAMER(Feld[x][y]) ||
2614          IS_POLAR(Feld[x][y]) ||
2615          IS_POLAR_CROSS(Feld[x][y])) && x == ELX && y == ELY)
2616     {
2617       check = 0;
2618
2619       if (IS_BEAMER(Feld[x][y]))
2620       {
2621 #if 0
2622         printf("TEST (%d, %d) [%d] [%d]\n",
2623                LX, LY,
2624                laser.beamer_edge, laser.beamer[1].num);
2625 #endif
2626
2627         laser.num_edges--;
2628       }
2629
2630       ScanLaser();
2631     }
2632
2633     if (check == 2)
2634       DrawLaser(0, DL_LASER_ENABLED);
2635   }
2636 }
2637
2638 void AutoRotateMirrors()
2639 {
2640   static unsigned int rotate_delay = 0;
2641   int x, y;
2642
2643   if (!DelayReached(&rotate_delay, AUTO_ROTATE_DELAY))
2644     return;
2645
2646   for (x = 0; x < lev_fieldx; x++)
2647   {
2648     for (y = 0; y < lev_fieldy; y++)
2649     {
2650       int element = Feld[x][y];
2651
2652       /* do not rotate objects hit by the laser after the game was solved */
2653       if (game_mm.level_solved && Hit[x][y])
2654         continue;
2655
2656       if (IS_DF_MIRROR_AUTO(element) ||
2657           IS_GRID_WOOD_AUTO(element) ||
2658           IS_GRID_STEEL_AUTO(element) ||
2659           element == EL_REFRACTOR)
2660         RotateMirror(x, y, MB_RIGHTBUTTON);
2661     }
2662   }
2663 }
2664
2665 boolean ObjHit(int obx, int oby, int bits)
2666 {
2667   int i;
2668
2669   obx *= TILEX;
2670   oby *= TILEY;
2671
2672   if (bits & HIT_POS_CENTER)
2673   {
2674     if (ReadPixel(drawto, SX + obx + 15, SY + oby + 15) == pen_ray)
2675       return TRUE;
2676   }
2677
2678   if (bits & HIT_POS_EDGE)
2679   {
2680     for (i = 0; i < 4; i++)
2681       if (ReadPixel(drawto,
2682                     SX + obx + 31 * (i % 2),
2683                     SY + oby + 31 * (i / 2)) == pen_ray)
2684         return TRUE;
2685   }
2686
2687   if (bits & HIT_POS_BETWEEN)
2688   {
2689     for (i = 0; i < 4; i++)
2690       if (ReadPixel(drawto,
2691                     SX + 4 + obx + 22 * (i % 2),
2692                     SY + 4 + oby + 22 * (i / 2)) == pen_ray)
2693         return TRUE;
2694   }
2695
2696   return FALSE;
2697 }
2698
2699 void DeletePacMan(int px, int py)
2700 {
2701   int i, j;
2702
2703   Bang_MM(px, py);
2704
2705   if (game_mm.num_pacman <= 1)
2706   {
2707     game_mm.num_pacman = 0;
2708     return;
2709   }
2710
2711   for (i = 0; i < game_mm.num_pacman; i++)
2712     if (game_mm.pacman[i].x == px && game_mm.pacman[i].y == py)
2713       break;
2714
2715   game_mm.num_pacman--;
2716
2717   for (j = i; j < game_mm.num_pacman; j++)
2718   {
2719     game_mm.pacman[j].x   = game_mm.pacman[j + 1].x;
2720     game_mm.pacman[j].y   = game_mm.pacman[j + 1].y;
2721     game_mm.pacman[j].dir = game_mm.pacman[j + 1].dir;
2722   }
2723 }
2724
2725 void ColorCycling(void)
2726 {
2727   static int CC, Cc = 0;
2728
2729   static int color, old = 0xF00, new = 0x010, mult = 1;
2730   static unsigned short red, green, blue;
2731
2732   if (color_status == STATIC_COLORS)
2733     return;
2734
2735   CC = Counter();
2736
2737   if (CC < Cc || CC > Cc + 50)
2738   {
2739     Cc = CC;
2740
2741     color = old + new * mult;
2742     if (mult > 0)
2743       mult++;
2744     else
2745       mult--;
2746
2747     if (ABS(mult) == 16)
2748     {
2749       mult =- mult / 16;
2750       old = color;
2751       new = new << 4;
2752
2753       if (new > 0x100)
2754         new = 0x001;
2755     }
2756
2757     red   = 0x0e00 * ((color & 0xF00) >> 8);
2758     green = 0x0e00 * ((color & 0x0F0) >> 4);
2759     blue  = 0x0e00 * ((color & 0x00F));
2760     SetRGB(pen_magicolor[0], red, green, blue);
2761
2762     red   = 0x1111 * ((color & 0xF00) >> 8);
2763     green = 0x1111 * ((color & 0x0F0) >> 4);
2764     blue  = 0x1111 * ((color & 0x00F));
2765     SetRGB(pen_magicolor[1], red, green, blue);
2766   }
2767 }
2768
2769 static void GameActions_MM_Ext(byte action[MAX_PLAYERS], boolean warp_mode)
2770 {
2771   static unsigned int action_delay = 0;
2772   static unsigned int pacman_delay = 0;
2773   static unsigned int energy_delay = 0;
2774   static unsigned int overload_delay = 0;
2775   int element;
2776   int x, y, i;
2777
2778   int r, d;
2779
2780   WaitUntilDelayReached(&action_delay, GameFrameDelay);
2781
2782   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2783     Stop[x][y] = FALSE;
2784
2785   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2786   {
2787     element = Feld[x][y];
2788
2789     if (!IS_MOVING(x, y) && CAN_MOVE(element))
2790       StartMoving_MM(x, y);
2791     else if (IS_MOVING(x, y))
2792       ContinueMoving_MM(x, y);
2793     else if (IS_EXPLODING(element))
2794       Explode_MM(x, y, Frame[x][y], EX_NORMAL);
2795     else if (element == EL_EXIT_OPENING)
2796       OpenExit(x, y);
2797     else if (element == EL_GRAY_BALL_OPENING)
2798       OpenSurpriseBall(x, y);
2799     else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_ICE)
2800       MeltIce(x, y);
2801     else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_AMOEBA)
2802       GrowAmoeba(x, y);
2803   }
2804
2805   AutoRotateMirrors();
2806
2807 #if 1
2808   /* !!! CHANGE THIS: REDRAW ONLY WHEN NEEDED !!! */
2809
2810   /* redraw after Explode_MM() ... */
2811   if (laser.redraw)
2812     DrawLaser(0, DL_LASER_ENABLED);
2813   laser.redraw = FALSE;
2814 #endif
2815
2816   CT = Counter();
2817
2818   if (game_mm.num_pacman && DelayReached(&pacman_delay, 250))
2819   {
2820     MovePacMen();
2821
2822     if (laser.num_damages > MAX_LASER_LEN && !laser.fuse_off)
2823     {
2824       DrawLaser(0, DL_LASER_DISABLED);
2825       ScanLaser();
2826     }
2827   }
2828
2829   if (DelayReached(&energy_delay, 4000))
2830   {
2831     game_mm.energy_left--;
2832     if (game_mm.energy_left >= 0)
2833     {
2834 #if 0
2835       BlitBitmap(pix[PIX_DOOR], drawto,
2836                  DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
2837                  ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
2838                  DX_ENERGY, DY_ENERGY);
2839 #endif
2840       redraw_mask |= REDRAW_DOOR_1;
2841     }
2842     else if (setup.time_limit)
2843     {
2844       int i;
2845
2846       for (i = 15; i >= 0; i--)
2847       {
2848 #if 0
2849         SetRGB(pen_ray, 0x0000, 0x0000, i * color_scale);
2850 #endif
2851         pen_ray = GetPixelFromRGB(window,
2852                                   native_mm_level.laser_red   * 0x11 * i,
2853                                   native_mm_level.laser_green * 0x11 * i,
2854                                   native_mm_level.laser_blue  * 0x11 * i);
2855
2856         DrawLaser(0, DL_LASER_ENABLED);
2857         BackToFront();
2858         Delay(50);
2859       }
2860
2861       StopSound(SND_WARNTON);
2862       FadeMusic();
2863
2864       DrawLaser(0, DL_LASER_DISABLED);
2865       game_mm.game_over = TRUE;
2866       game_mm.game_over_cause = GAME_OVER_NO_ENERGY;
2867
2868 #if 0
2869       if (Request("Out of magic energy ! Play it again ?",
2870                   REQ_ASK | REQ_STAY_CLOSED))
2871       {
2872         InitGame();
2873       }
2874       else
2875       {
2876         game_status = MAINMENU;
2877         DrawMainMenu();
2878       }
2879 #endif
2880
2881       return;
2882     }
2883   }
2884
2885   element = laser.dest_element;
2886
2887 #if 0
2888   if (element != Feld[ELX][ELY])
2889   {
2890     printf("element == %d, Feld[ELX][ELY] == %d\n",
2891            element, Feld[ELX][ELY]);
2892   }
2893 #endif
2894
2895   if (!laser.overloaded && laser.overload_value == 0 &&
2896       element != EL_BOMB &&
2897       element != EL_MINE &&
2898       element != EL_BALL_GRAY &&
2899       element != EL_BLOCK_STONE &&
2900       element != EL_BLOCK_WOOD &&
2901       element != EL_FUSE_ON &&
2902       element != EL_FUEL_FULL &&
2903       !IS_WALL_ICE(element) &&
2904       !IS_WALL_AMOEBA(element))
2905     return;
2906
2907   if (((laser.overloaded && laser.overload_value < MAX_LASER_OVERLOAD) ||
2908        (!laser.overloaded && laser.overload_value > 0)) &&
2909       DelayReached(&overload_delay, 60 + !laser.overloaded * 120))
2910   {
2911     if (laser.overloaded)
2912       laser.overload_value++;
2913     else
2914       laser.overload_value--;
2915
2916     if (game_mm.cheat_no_overload)
2917     {
2918       laser.overloaded = FALSE;
2919       laser.overload_value = 0;
2920     }
2921
2922     game_mm.laser_overload_value = laser.overload_value;
2923
2924     if (laser.overload_value < MAX_LASER_OVERLOAD - 8)
2925     {
2926       int color_up = 0xFF * laser.overload_value / MAX_LASER_OVERLOAD;
2927       int color_down = 0xFF - color_up;
2928
2929 #if 0
2930       SetRGB(pen_ray, (laser.overload_value / 6) * color_scale, 0x0000,
2931              (15 - (laser.overload_value / 6)) * color_scale);
2932 #endif
2933       pen_ray =
2934         GetPixelFromRGB(window,
2935                         (native_mm_level.laser_red  ? 0xFF : color_up),
2936                         (native_mm_level.laser_green ? color_down : 0x00),
2937                         (native_mm_level.laser_blue  ? color_down : 0x00));
2938
2939       DrawLaser(0, DL_LASER_ENABLED);
2940       BackToFront();
2941     }
2942
2943     if (laser.overloaded)
2944     {
2945       if (setup.sound_loops)
2946         PlaySoundExt(SND_WARNTON, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
2947                      SND_CTRL_PLAY_LOOP);
2948       else
2949         PlaySoundStereo(SND_WARNTON, SOUND_MAX_RIGHT);
2950     }
2951
2952     if (!laser.overloaded)
2953       StopSound(SND_WARNTON);
2954
2955     if (laser.overloaded)
2956     {
2957 #if 0
2958       BlitBitmap(pix[PIX_DOOR], drawto,
2959                  DOOR_GFX_PAGEX4 + XX_OVERLOAD,
2960                  DOOR_GFX_PAGEY1 + YY_OVERLOAD + OVERLOAD_YSIZE
2961                  - laser.overload_value,
2962                  OVERLOAD_XSIZE, laser.overload_value,
2963                  DX_OVERLOAD, DY_OVERLOAD + OVERLOAD_YSIZE
2964                  - laser.overload_value);
2965 #endif
2966       redraw_mask |= REDRAW_DOOR_1;
2967     }
2968     else
2969     {
2970 #if 0
2971       BlitBitmap(pix[PIX_DOOR], drawto,
2972                  DOOR_GFX_PAGEX5 + XX_OVERLOAD, DOOR_GFX_PAGEY1 + YY_OVERLOAD,
2973                  OVERLOAD_XSIZE, OVERLOAD_YSIZE - laser.overload_value,
2974                  DX_OVERLOAD, DY_OVERLOAD);
2975 #endif
2976       redraw_mask |= REDRAW_DOOR_1;
2977     }
2978
2979     if (laser.overload_value == MAX_LASER_OVERLOAD)
2980     {
2981       int i;
2982
2983       for (i = 15; i >= 0; i--)
2984       {
2985 #if 0
2986         SetRGB(pen_ray, i * color_scale, 0x0000, 0x0000);
2987 #endif
2988
2989         pen_ray = GetPixelFromRGB(window, 0x11 * i, 0x00, 0x00);
2990
2991         DrawLaser(0, DL_LASER_ENABLED);
2992         BackToFront();
2993         Delay(50);
2994       }
2995
2996       DrawLaser(0, DL_LASER_DISABLED);
2997
2998       game_mm.game_over = TRUE;
2999       game_mm.game_over_cause = GAME_OVER_OVERLOADED;
3000
3001 #if 0
3002       if (Request("Magic spell hit Mc Duffin ! Play it again ?",
3003                   REQ_ASK | REQ_STAY_CLOSED))
3004       {
3005         InitGame();
3006       }
3007       else
3008       {
3009         game_status = MAINMENU;
3010         DrawMainMenu();
3011       }
3012 #endif
3013
3014       return;
3015     }
3016   }
3017
3018   if (laser.fuse_off)
3019     return;
3020
3021   CT -= Ct;
3022
3023   if (element == EL_BOMB && CT > 1500)
3024   {
3025     if (game_mm.cheat_no_explosion)
3026       return;
3027
3028 #if 0
3029     laser.num_damages--;
3030     DrawLaser(0, DL_LASER_DISABLED);
3031     laser.num_edges = 0;
3032 #endif
3033
3034     Bang_MM(ELX, ELY);
3035
3036     laser.dest_element = EL_EXPLODING_OPAQUE;
3037
3038 #if 0
3039     Bang_MM(ELX, ELY);
3040     laser.num_damages--;
3041     DrawLaser(0, DL_LASER_DISABLED);
3042
3043     laser.num_edges = 0;
3044     Bang_MM(laser.start_edge.x, laser.start_edge.y);
3045
3046     if (Request("Bomb killed Mc Duffin ! Play it again ?",
3047                 REQ_ASK | REQ_STAY_CLOSED))
3048     {
3049       InitGame();
3050     }
3051     else
3052     {
3053       game_status = MAINMENU;
3054       DrawMainMenu();
3055     }
3056 #endif
3057
3058     return;
3059   }
3060
3061   if (element == EL_FUSE_ON && CT > 500)
3062   {
3063     laser.fuse_off = TRUE;
3064     laser.fuse_x = ELX;
3065     laser.fuse_y = ELY;
3066
3067     DrawLaser(0, DL_LASER_DISABLED);
3068     DrawGraphic_MM(ELX, ELY, IMG_MM_FUSE);
3069   }
3070
3071   if (element == EL_BALL_GRAY && CT > 1500)
3072   {
3073     static int new_elements[] =
3074     {
3075       EL_MIRROR_START,
3076       EL_MIRROR_FIXED_START,
3077       EL_POLAR_START,
3078       EL_POLAR_CROSS_START,
3079       EL_PACMAN_START,
3080       EL_KETTLE,
3081       EL_BOMB,
3082       EL_PRISM
3083     };
3084     int num_new_elements = sizeof(new_elements) / sizeof(int);
3085     int new_element = new_elements[RND(num_new_elements)];
3086
3087     Store[ELX][ELY] = new_element + RND(get_num_elements(new_element));
3088     Feld[ELX][ELY] = EL_GRAY_BALL_OPENING;
3089
3090     /* !!! CHECK AGAIN: Laser on Polarizer !!! */
3091     ScanLaser();
3092
3093     return;
3094
3095 #if 0
3096     int graphic;
3097
3098     switch (RND(5))
3099     {
3100       case 0:
3101         element = EL_MIRROR_START + RND(16);
3102         break;
3103       case 1:
3104         {
3105           int rnd = RND(3);
3106
3107           element = (rnd == 0 ? EL_KETTLE : rnd == 1 ? EL_BOMB : EL_PRISM);
3108         }
3109         break;
3110       default:
3111         {
3112           int rnd = RND(3);
3113
3114           element = (rnd == 0 ? EL_FUSE_ON :
3115                      rnd >= 1 && rnd <= 4 ? EL_PACMAN_RIGHT + rnd - 1 :
3116                      rnd >= 5 && rnd <= 20 ? EL_POLAR_START + rnd - 5 :
3117                      rnd >= 21 && rnd <= 24 ? EL_POLAR_CROSS_START + rnd - 21 :
3118                      EL_MIRROR_FIXED_START + rnd - 25);
3119         }
3120         break;
3121     }
3122
3123     graphic = el2gfx(element);
3124
3125     for (i = 0; i < 50; i++)
3126     {
3127       int x = RND(26);
3128       int y = RND(26);
3129
3130 #if 0
3131       BlitBitmap(pix[PIX_BACK], drawto,
3132                  SX + (graphic % GFX_PER_LINE) * TILEX + x,
3133                  SY + (graphic / GFX_PER_LINE) * TILEY + y, 6, 6,
3134                  SX + ELX * TILEX + x,
3135                  SY + ELY * TILEY + y);
3136 #endif
3137       MarkTileDirty(ELX, ELY);
3138       BackToFront();
3139
3140       DrawLaser(0, DL_LASER_ENABLED);
3141
3142       Delay(50);
3143     }
3144
3145     Feld[ELX][ELY] = element;
3146     DrawField_MM(ELX, ELY);
3147
3148 #if 0
3149     printf("NEW ELEMENT: (%d, %d)\n", ELX, ELY);
3150 #endif
3151
3152     /* above stuff: GRAY BALL -> PRISM !!! */
3153 /*
3154     LX = ELX * TILEX + 14;
3155     LY = ELY * TILEY + 14;
3156     if (laser.current_angle == (laser.current_angle >> 1) << 1)
3157       OK = 8;
3158     else
3159       OK = 4;
3160     LX -= OK * XS;
3161     LY -= OK * YS;
3162
3163     laser.num_edges -= 2;
3164     laser.num_damages--;
3165 */
3166
3167 #if 0
3168     for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
3169       if (laser.damage[i].is_mirror)
3170         break;
3171
3172     if (i > 0)
3173       DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3174     else
3175       DrawLaser(0, DL_LASER_DISABLED);
3176 #else
3177     DrawLaser(0, DL_LASER_DISABLED);
3178 #endif
3179
3180     ScanLaser();
3181
3182     /*
3183     printf("TEST ELEMENT: %d\n", Feld[0][0]);
3184     */
3185 #endif
3186
3187     return;
3188   }
3189
3190   if (IS_WALL_ICE(element) && CT > 1000)
3191   {
3192     PlaySoundStereo(SND_SLURP, ST(ELX));
3193
3194     {
3195       Feld[ELX][ELY] = Feld[ELX][ELY] - EL_WALL_ICE + EL_WALL_CHANGING;
3196       Store[ELX][ELY] = EL_WALL_ICE;
3197       Store2[ELX][ELY] = laser.wall_mask;
3198
3199       laser.dest_element = Feld[ELX][ELY];
3200
3201       return;
3202     }
3203
3204     for (i = 0; i < 5; i++)
3205     {
3206       int phase = i + 1;
3207
3208       if (i == 4)
3209       {
3210         Feld[ELX][ELY] &= (laser.wall_mask ^ 0xFF);
3211         phase = 0;
3212       }
3213
3214       DrawWallsAnimation_MM(ELX, ELY, Feld[ELX][ELY], phase, laser.wall_mask);
3215       BackToFront();
3216       Delay(100);
3217     }
3218
3219     if (Feld[ELX][ELY] == EL_WALL_ICE)
3220       Feld[ELX][ELY] = EL_EMPTY;
3221
3222 /*
3223     laser.num_edges--;
3224     LX = laser.edge[laser.num_edges].x - (SX + 2);
3225     LY = laser.edge[laser.num_edges].y - (SY + 2);
3226 */
3227
3228     for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
3229       if (laser.damage[i].is_mirror)
3230         break;
3231
3232     if (i > 0)
3233       DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3234     else
3235       DrawLaser(0, DL_LASER_DISABLED);
3236
3237     ScanLaser();
3238
3239     return;
3240   }
3241
3242   if (IS_WALL_AMOEBA(element) && CT > 1200)
3243   {
3244     int k1, k2, k3, dx, dy, de, dm;
3245     int element2 = Feld[ELX][ELY];
3246
3247     if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element))
3248       return;
3249
3250     for (i = laser.num_damages - 1; i >= 0; i--)
3251       if (laser.damage[i].is_mirror)
3252         break;
3253
3254     r = laser.num_edges;
3255     d = laser.num_damages;
3256     k1 = i;
3257
3258     if (k1 > 0)
3259     {
3260       int x, y;
3261
3262       DrawLaser(laser.damage[k1].edge - 1, DL_LASER_DISABLED);
3263
3264       laser.num_edges++;
3265       DrawLaser(0, DL_LASER_ENABLED);
3266       laser.num_edges--;
3267
3268       x = laser.damage[k1].x;
3269       y = laser.damage[k1].y;
3270
3271       DrawField_MM(x, y);
3272     }
3273
3274     for (i = 0; i < 4; i++)
3275     {
3276       if (laser.wall_mask & (1 << i))
3277       {
3278         if (ReadPixel(drawto,
3279                       SX + ELX * TILEX + 14 + (i % 2) * 2,
3280                       SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3281           break;
3282         if (ReadPixel(drawto,
3283                       SX + ELX * TILEX + 31 * (i % 2),
3284                       SY + ELY * TILEY + 14 + (i / 2) * 2) == pen_ray)
3285           break;
3286       }
3287     }
3288
3289     k2 = i;
3290
3291     for (i = 0; i < 4; i++)
3292     {
3293       if (laser.wall_mask & (1 << i))
3294       {
3295         if (ReadPixel(drawto,
3296                       SX + ELX * TILEX + 31 * (i % 2),
3297                       SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3298           break;
3299       }
3300     }
3301
3302     k3 = i;
3303
3304     if (laser.num_beamers > 0 ||
3305         k1 < 1 || k2 < 4 || k3 < 4 ||
3306         ReadPixel(drawto, SX + ELX * TILEX + 14, SY + ELY * TILEY + 14)
3307         == pen_ray)
3308     {
3309       laser.num_edges = r;
3310       laser.num_damages = d;
3311
3312       DrawLaser(0, DL_LASER_DISABLED);
3313     }
3314
3315     Feld[ELX][ELY] = element | laser.wall_mask;
3316
3317     dx = ELX;
3318     dy = ELY;
3319     de = Feld[ELX][ELY];
3320     dm = laser.wall_mask;
3321
3322 #if 1
3323     {
3324       int x = ELX, y = ELY;
3325       int wall_mask = laser.wall_mask;
3326
3327       ScanLaser();
3328       DrawLaser(0, DL_LASER_ENABLED);
3329
3330       PlaySoundStereo(SND_AMOEBE, ST(dx));
3331
3332       Feld[x][y] = Feld[x][y] - EL_WALL_AMOEBA + EL_WALL_CHANGING;
3333       Store[x][y] = EL_WALL_AMOEBA;
3334       Store2[x][y] = wall_mask;
3335
3336       return;
3337     }
3338 #endif
3339
3340     DrawWallsAnimation_MM(dx, dy, de, 4, dm);
3341     ScanLaser();
3342     DrawLaser(0, DL_LASER_ENABLED);
3343
3344     PlaySoundStereo(SND_AMOEBE, ST(dx));
3345
3346     for (i = 4; i >= 0; i--)
3347     {
3348       DrawWallsAnimation_MM(dx, dy, de, i, dm);
3349
3350       BackToFront();
3351       Delay(20);
3352     }
3353
3354     DrawLaser(0, DL_LASER_ENABLED);
3355
3356     return;
3357   }
3358
3359   if ((element == EL_BLOCK_WOOD || element == EL_BLOCK_STONE) &&
3360       laser.stops_inside_element && CT > 1500)
3361   {
3362     int x, y;
3363     int k;
3364
3365     if (ABS(XS) > ABS(YS))
3366       k = 0;
3367     else
3368       k = 1;
3369     if (XS < YS)
3370       k += 2;
3371
3372     for (i = 0; i < 4; i++)
3373     {
3374       if (i)
3375         k++;
3376       if (k > 3)
3377         k = 0;
3378
3379       x = ELX + Step[k * 4].x;
3380       y = ELY + Step[k * 4].y;
3381
3382       if (!IN_LEV_FIELD(x, y) || Feld[x][y] != EL_EMPTY)
3383         continue;
3384
3385       if (ObjHit(x, y, HIT_POS_CENTER | HIT_POS_EDGE | HIT_POS_BETWEEN))
3386         continue;
3387
3388       break;
3389     }
3390
3391     if (i > 3)
3392     {
3393       laser.overloaded = (element == EL_BLOCK_STONE);
3394
3395       return;
3396     }
3397
3398     PlaySoundStereo(SND_BONG, ST(ELX));
3399
3400     Feld[ELX][ELY] = 0;
3401     Feld[x][y] = element;
3402
3403     DrawGraphic_MM(ELX, ELY, IMG_EMPTY);
3404     DrawField_MM(x, y);
3405
3406     if (element == EL_BLOCK_STONE && Box[ELX][ELY])
3407     {
3408       DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
3409       DrawLaser(laser.num_edges - 1, DL_LASER_ENABLED);
3410     }
3411
3412     ScanLaser();
3413
3414     return;
3415   }
3416
3417   if (element == EL_FUEL_FULL && CT > 200)
3418   {
3419     for (i = game_mm.energy_left; i <= MAX_LASER_ENERGY; i+=2)
3420     {
3421 #if 0
3422       BlitBitmap(pix[PIX_DOOR], drawto,
3423                  DOOR_GFX_PAGEX4 + XX_ENERGY,
3424                  DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
3425                  ENERGY_XSIZE, i, DX_ENERGY,
3426                  DY_ENERGY + ENERGY_YSIZE - i);
3427 #endif
3428
3429       redraw_mask |= REDRAW_DOOR_1;
3430       BackToFront();
3431
3432       Delay(20);
3433     }
3434
3435     game_mm.energy_left = MAX_LASER_ENERGY;
3436     Feld[ELX][ELY] = EL_FUEL_EMPTY;
3437     DrawField_MM(ELX, ELY);
3438
3439     DrawLaser(0, DL_LASER_ENABLED);
3440
3441     return;
3442   }
3443
3444   return;
3445 }
3446
3447 void GameActions_MM(byte action[MAX_PLAYERS], boolean warp_mode)
3448 {
3449   if (!button_status)
3450     ClickElement(0, 0, MB_NOT_PRESSED);
3451
3452   GameActions_MM_Ext(action, warp_mode);
3453 }
3454
3455 void MovePacMen()
3456 {
3457   static int p = -1;
3458   int mx, my, ox, oy, nx, ny;
3459   int element;
3460   int l;
3461
3462   if (++p >= game_mm.num_pacman)
3463     p = 0;
3464
3465   game_mm.pacman[p].dir--;
3466
3467   for (l = 1; l < 5; l++)
3468   {
3469     game_mm.pacman[p].dir++;
3470
3471     if (game_mm.pacman[p].dir > 4)
3472       game_mm.pacman[p].dir = 1;
3473
3474     if (game_mm.pacman[p].dir % 2)
3475     {
3476       mx = 0;
3477       my = game_mm.pacman[p].dir - 2;
3478     }
3479     else
3480     {
3481       my = 0;
3482       mx = 3 - game_mm.pacman[p].dir;
3483     }
3484
3485     ox = game_mm.pacman[p].x;
3486     oy = game_mm.pacman[p].y;
3487     nx = ox + mx;
3488     ny = oy + my;
3489     element = Feld[nx][ny];
3490
3491     if (nx < 0 || nx > 15 || ny < 0 || ny > 11)
3492       continue;
3493
3494     if (!IS_EATABLE4PACMAN(element))
3495       continue;
3496
3497     if (ObjHit(nx, ny, HIT_POS_CENTER))
3498       continue;
3499
3500     Feld[ox][oy] = EL_EMPTY;
3501     Feld[nx][ny] =
3502       EL_PACMAN_RIGHT - 1 +
3503       (game_mm.pacman[p].dir - 1 +
3504        (game_mm.pacman[p].dir % 2) * 2);
3505
3506     game_mm.pacman[p].x = nx;
3507     game_mm.pacman[p].y = ny;
3508
3509     DrawGraphic_MM(ox, oy, IMG_EMPTY);
3510
3511     if (element != EL_EMPTY)
3512     {
3513       int graphic = el2gfx(Feld[nx][ny]);
3514       Bitmap *bitmap;
3515       int src_x, src_y;
3516       int i;
3517
3518       getGraphicSource(graphic, 0, &bitmap, &src_x, &src_y);
3519
3520       CT = Counter();
3521       ox = SX + ox * TILEX;
3522       oy = SY + oy * TILEY;
3523
3524       for (i = 1; i < 33; i += 2)
3525         BlitBitmap(bitmap, window,
3526                    src_x, src_y, TILEX, TILEY,
3527                    ox + i * mx, oy + i * my);
3528       Ct = Ct + Counter() - CT;
3529     }
3530
3531     DrawField_MM(nx, ny);
3532     BackToFront();
3533
3534     if (!laser.fuse_off)
3535     {
3536       DrawLaser(0, DL_LASER_ENABLED);
3537
3538       if (ObjHit(nx, ny, HIT_POS_BETWEEN))
3539       {
3540         AddDamagedField(nx, ny);
3541
3542         laser.damage[laser.num_damages - 1].edge = 0;
3543       }
3544     }
3545
3546     if (element == EL_BOMB)
3547       DeletePacMan(nx, ny);
3548
3549     if (IS_WALL_AMOEBA(element) &&
3550         (LX + 2 * XS) / TILEX == nx &&
3551         (LY + 2 * YS) / TILEY == ny)
3552     {
3553       laser.num_edges--;
3554       ScanLaser();
3555     }
3556
3557     break;
3558   }
3559 }
3560
3561 void GameWon_MM()
3562 {
3563   int hi_pos;
3564   boolean raise_level = FALSE;
3565
3566 #if 0
3567   if (local_player->MovPos)
3568     return;
3569
3570   local_player->LevelSolved = FALSE;
3571 #endif
3572
3573   if (game_mm.energy_left)
3574   {
3575     if (setup.sound_loops)
3576       PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3577                    SND_CTRL_PLAY_LOOP);
3578
3579     while (game_mm.energy_left > 0)
3580     {
3581       if (!setup.sound_loops)
3582         PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3583
3584       /*
3585       if (game_mm.energy_left > 0 && !(game_mm.energy_left % 10))
3586         RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3587       */
3588
3589       RaiseScore_MM(5);
3590
3591       game_mm.energy_left--;
3592       if (game_mm.energy_left >= 0)
3593       {
3594 #if 0
3595         BlitBitmap(pix[PIX_DOOR], drawto,
3596                    DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
3597                    ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
3598                    DX_ENERGY, DY_ENERGY);
3599 #endif
3600         redraw_mask |= REDRAW_DOOR_1;
3601       }
3602
3603       BackToFront();
3604       Delay(10);
3605     }
3606
3607     if (setup.sound_loops)
3608       StopSound(SND_SIRR);
3609   }
3610   else if (native_mm_level.time == 0)           /* level without time limit */
3611   {
3612     if (setup.sound_loops)
3613       PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
3614                    SND_CTRL_PLAY_LOOP);
3615
3616     while (TimePlayed < 999)
3617     {
3618       if (!setup.sound_loops)
3619         PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3620       if (TimePlayed < 999 && !(TimePlayed % 10))
3621         RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3622       if (TimePlayed < 900 && !(TimePlayed % 10))
3623         TimePlayed += 10;
3624       else
3625         TimePlayed++;
3626
3627       /*
3628       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
3629       */
3630
3631       BackToFront();
3632       Delay(10);
3633     }
3634
3635     if (setup.sound_loops)
3636       StopSound(SND_SIRR);
3637   }
3638
3639 #if 0
3640   FadeSounds();
3641 #endif
3642
3643   CloseDoor(DOOR_CLOSE_1);
3644
3645   Request("Level solved !", REQ_CONFIRM);
3646
3647   if (level_nr == leveldir_current->handicap_level)
3648   {
3649     leveldir_current->handicap_level++;
3650     SaveLevelSetup_SeriesInfo();
3651   }
3652
3653   if (level_editor_test_game)
3654     game_mm.score = -1;         /* no highscore when playing from editor */
3655   else if (level_nr < leveldir_current->last_level)
3656     raise_level = TRUE;         /* advance to next level */
3657
3658   if ((hi_pos = NewHiScore_MM()) >= 0)
3659   {
3660     game_status = HALLOFFAME;
3661
3662     // DrawHallOfFame(hi_pos);
3663
3664     if (raise_level)
3665       level_nr++;
3666   }
3667   else
3668   {
3669     game_status = MAINMENU;
3670
3671     if (raise_level)
3672       level_nr++;
3673
3674     // DrawMainMenu();
3675   }
3676
3677   BackToFront();
3678 }
3679
3680 int NewHiScore_MM()
3681 {
3682   int k, l;
3683   int position = -1;
3684
3685   // LoadScore(level_nr);
3686
3687   if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
3688       game_mm.score < highscore[MAX_SCORE_ENTRIES - 1].Score)
3689     return -1;
3690
3691   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3692   {
3693     if (game_mm.score > highscore[k].Score)
3694     {
3695       /* player has made it to the hall of fame */
3696
3697       if (k < MAX_SCORE_ENTRIES - 1)
3698       {
3699         int m = MAX_SCORE_ENTRIES - 1;
3700
3701 #ifdef ONE_PER_NAME
3702         for (l = k; l < MAX_SCORE_ENTRIES; l++)
3703           if (!strcmp(setup.player_name, highscore[l].Name))
3704             m = l;
3705         if (m == k)     /* player's new highscore overwrites his old one */
3706           goto put_into_list;
3707 #endif
3708
3709         for (l = m; l>k; l--)
3710         {
3711           strcpy(highscore[l].Name, highscore[l - 1].Name);
3712           highscore[l].Score = highscore[l - 1].Score;
3713         }
3714       }
3715
3716 #ifdef ONE_PER_NAME
3717       put_into_list:
3718 #endif
3719       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3720       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3721       highscore[k].Score = game_mm.score;
3722       position = k;
3723
3724       break;
3725     }
3726
3727 #ifdef ONE_PER_NAME
3728     else if (!strncmp(setup.player_name, highscore[k].Name,
3729                       MAX_PLAYER_NAME_LEN))
3730       break;    /* player already there with a higher score */
3731 #endif
3732
3733   }
3734
3735   // if (position >= 0)
3736   //   SaveScore(level_nr);
3737
3738   return position;
3739 }
3740
3741 static void InitMovingField_MM(int x, int y, int direction)
3742 {
3743   int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3744   int newy = y + (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3745
3746   MovDir[x][y] = direction;
3747   MovDir[newx][newy] = direction;
3748
3749   if (Feld[newx][newy] == EL_EMPTY)
3750     Feld[newx][newy] = EL_BLOCKED;
3751 }
3752
3753 static void Moving2Blocked_MM(int x, int y, int *goes_to_x, int *goes_to_y)
3754 {
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);
3758
3759   *goes_to_x = newx;
3760   *goes_to_y = newy;
3761 }
3762
3763 static void Blocked2Moving_MM(int x, int y,
3764                               int *comes_from_x, int *comes_from_y)
3765 {
3766   int oldx = x, oldy = y;
3767   int direction = MovDir[x][y];
3768
3769   if (direction == MV_LEFT)
3770     oldx++;
3771   else if (direction == MV_RIGHT)
3772     oldx--;
3773   else if (direction == MV_UP)
3774     oldy++;
3775   else if (direction == MV_DOWN)
3776     oldy--;
3777
3778   *comes_from_x = oldx;
3779   *comes_from_y = oldy;
3780 }
3781
3782 static int MovingOrBlocked2Element_MM(int x, int y)
3783 {
3784   int element = Feld[x][y];
3785
3786   if (element == EL_BLOCKED)
3787   {
3788     int oldx, oldy;
3789
3790     Blocked2Moving_MM(x, y, &oldx, &oldy);
3791
3792     return Feld[oldx][oldy];
3793   }
3794
3795   return element;
3796 }
3797
3798 #if 0
3799 static void RemoveField(int x, int y)
3800 {
3801   Feld[x][y] = EL_EMPTY;
3802   MovPos[x][y] = 0;
3803   MovDir[x][y] = 0;
3804   MovDelay[x][y] = 0;
3805 }
3806 #endif
3807
3808 static void RemoveMovingField_MM(int x, int y)
3809 {
3810   int oldx = x, oldy = y, newx = x, newy = y;
3811
3812   if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
3813     return;
3814
3815   if (IS_MOVING(x, y))
3816   {
3817     Moving2Blocked_MM(x, y, &newx, &newy);
3818     if (Feld[newx][newy] != EL_BLOCKED)
3819       return;
3820   }
3821   else if (Feld[x][y] == EL_BLOCKED)
3822   {
3823     Blocked2Moving_MM(x, y, &oldx, &oldy);
3824     if (!IS_MOVING(oldx, oldy))
3825       return;
3826   }
3827
3828   Feld[oldx][oldy] = EL_EMPTY;
3829   Feld[newx][newy] = EL_EMPTY;
3830   MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
3831   MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
3832
3833   DrawLevelField_MM(oldx, oldy);
3834   DrawLevelField_MM(newx, newy);
3835 }
3836
3837 void PlaySoundLevel(int x, int y, int sound_nr)
3838 {
3839   int sx = SCREENX(x), sy = SCREENY(y);
3840   int volume, stereo;
3841   int silence_distance = 8;
3842
3843   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
3844       (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
3845     return;
3846
3847   if (!IN_LEV_FIELD(x, y) ||
3848       sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
3849       sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
3850     return;
3851
3852   volume = SOUND_MAX_VOLUME;
3853
3854 #ifndef MSDOS
3855   stereo = (sx - SCR_FIELDX/2) * 12;
3856 #else
3857   stereo = SOUND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
3858   if (stereo > SOUND_MAX_RIGHT)
3859     stereo = SOUND_MAX_RIGHT;
3860   if (stereo < SOUND_MAX_LEFT)
3861     stereo = SOUND_MAX_LEFT;
3862 #endif
3863
3864   if (!IN_SCR_FIELD(sx, sy))
3865   {
3866     int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
3867     int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
3868
3869     volume -= volume * (dx > dy ? dx : dy) / silence_distance;
3870   }
3871
3872   PlaySoundExt(sound_nr, volume, stereo, SND_CTRL_PLAY_SOUND);
3873 }
3874
3875 static void RaiseScore_MM(int value)
3876 {
3877   game_mm.score += value;
3878
3879 #if 0
3880   DrawText(DX_SCORE, DY_SCORE, int2str(game_mm.score, 4),
3881            FONT_TEXT_2);
3882 #endif
3883 }
3884
3885 void RaiseScoreElement_MM(int element)
3886 {
3887   switch(element)
3888   {
3889     case EL_PACMAN:
3890       RaiseScore_MM(native_mm_level.score[SC_PACMAN]);
3891       break;
3892
3893     case EL_KEY:
3894       RaiseScore_MM(native_mm_level.score[SC_KEY]);
3895       break;
3896
3897     default:
3898       break;
3899   }
3900 }