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