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