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