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