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