fixed animation of opening exit door (MM engine)
[rocksndiamonds.git] / src / game_mm / mm_game.c
1 // ============================================================================
2 // Mirror Magic -- McDuffin's Revenge
3 // ----------------------------------------------------------------------------
4 // (c) 1994-2017 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // mm_game.c
10 // ============================================================================
11
12 #include "main_mm.h"
13
14 #include "mm_main.h"
15 #include "mm_game.h"
16 #include "mm_tools.h"
17
18 /* graphic position values for game controls */
19 #define ENERGY_XSIZE            32
20 #define ENERGY_YSIZE            MAX_LASER_ENERGY
21 #define OVERLOAD_XSIZE          ENERGY_XSIZE
22 #define OVERLOAD_YSIZE          MAX_LASER_OVERLOAD
23
24 /* values for Explode_MM() */
25 #define EX_PHASE_START          0
26 #define EX_NORMAL               0
27 #define EX_KETTLE               1
28 #define EX_SHORT                2
29
30 /* special positions in the game control window (relative to control window) */
31 #define XX_LEVEL                36
32 #define YY_LEVEL                23
33 #define XX_KETTLES              29
34 #define YY_KETTLES              63
35 #define XX_SCORE                22
36 #define YY_SCORE                101
37 #define XX_ENERGY               8
38 #define YY_ENERGY               158
39 #define XX_OVERLOAD             60
40 #define YY_OVERLOAD             YY_ENERGY
41
42 /* special positions in the game control window (relative to main window) */
43 #define DX_LEVEL                (DX + XX_LEVEL)
44 #define DY_LEVEL                (DY + YY_LEVEL)
45 #define DX_KETTLES              (DX + XX_KETTLES)
46 #define DY_KETTLES              (DY + YY_KETTLES)
47 #define DX_SCORE                (DX + XX_SCORE)
48 #define DY_SCORE                (DY + YY_SCORE)
49 #define DX_ENERGY               (DX + XX_ENERGY)
50 #define DY_ENERGY               (DY + YY_ENERGY)
51 #define DX_OVERLOAD             (DX + XX_OVERLOAD)
52 #define DY_OVERLOAD             (DY + YY_OVERLOAD)
53
54 #define IS_LOOP_SOUND(s)        ((s) == SND_FUEL)
55 #define IS_MUSIC_SOUND(s)       ((s) == SND_TYGER || (s) == SND_VOYAGER)
56
57 /* game button identifiers */
58 #define GAME_CTRL_ID_LEFT               0
59 #define GAME_CTRL_ID_MIDDLE             1
60 #define GAME_CTRL_ID_RIGHT              2
61
62 #define NUM_GAME_BUTTONS                3
63
64 /* values for DrawLaser() */
65 #define DL_LASER_DISABLED               0
66 #define DL_LASER_ENABLED                1
67
68 /* values for 'click_delay_value' in ClickElement() */
69 #define CLICK_DELAY_SHORT       125
70 #define CLICK_DELAY_LONG        250
71 #define AUTO_ROTATE_DELAY       CLICK_DELAY_SHORT
72
73 /* forward declaration for internal use */
74 static int MovingOrBlocked2Element_MM(int, int);
75 static void Bang_MM(int, int);
76 static void RaiseScore_MM(int);
77 static void RemoveMovingField_MM(int, int);
78 static void InitMovingField_MM(int, int, int);
79 static void ContinueMoving_MM(int, int);
80 static void Moving2Blocked_MM(int, int, int *, int *);
81
82
83 static int get_element_angle(int element)
84 {
85   int element_phase = get_element_phase(element);
86
87   if (IS_MIRROR_FIXED(element) ||
88       IS_MCDUFFIN(element) ||
89       IS_LASER(element) ||
90       IS_RECEIVER(element))
91     return 4 * element_phase;
92   else
93     return element_phase;
94 }
95
96 static int get_opposite_angle(int angle)
97 {
98   int opposite_angle = angle + ANG_RAY_180;
99
100   /* make sure "opposite_angle" is in valid interval [0, 15] */
101   return (opposite_angle + 16) % 16;
102 }
103
104 static int get_mirrored_angle(int laser_angle, int mirror_angle)
105 {
106   int reflected_angle = 16 - laser_angle + mirror_angle;
107
108   /* make sure "reflected_angle" is in valid interval [0, 15] */
109   return (reflected_angle + 16) % 16;
110 }
111
112 static void InitMovDir_MM(int x, int y)
113 {
114   int element = Feld[x][y];
115   static int direction[3][4] =
116   {
117     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
118     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
119     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
120   };
121
122   switch(element)
123   {
124     case EL_PACMAN_RIGHT:
125     case EL_PACMAN_UP:
126     case EL_PACMAN_LEFT:
127     case EL_PACMAN_DOWN:
128       Feld[x][y] = EL_PACMAN;
129       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
130       break;
131
132     default:
133       break;
134   }
135 }
136
137 static void InitField(int x, int y, boolean init_game)
138 {
139   int element = Feld[x][y];
140
141   switch (element)
142   {
143     case EL_DF_EMPTY:
144       Feld[x][y] = EL_EMPTY;
145       break;
146
147     case EL_KETTLE:
148     case EL_CELL:
149       if (native_mm_level.auto_count_kettles)
150         game_mm.kettles_still_needed++;
151       break;
152
153     case EL_LIGHTBULB_OFF:
154       game_mm.lights_still_needed++;
155       break;
156
157     default:
158       if (IS_MIRROR(element) ||
159           IS_BEAMER_OLD(element) ||
160           IS_BEAMER(element) ||
161           IS_POLAR(element) ||
162           IS_POLAR_CROSS(element) ||
163           IS_DF_MIRROR(element) ||
164           IS_DF_MIRROR_AUTO(element) ||
165           IS_GRID_STEEL_AUTO(element) ||
166           IS_GRID_WOOD_AUTO(element) ||
167           IS_FIBRE_OPTIC(element))
168       {
169         if (IS_BEAMER_OLD(element))
170         {
171           Feld[x][y] = EL_BEAMER_BLUE_START + (element - EL_BEAMER_START);
172           element = Feld[x][y];
173         }
174
175         if (!IS_FIBRE_OPTIC(element))
176         {
177           static int steps_grid_auto = 0;
178
179           if (game_mm.num_cycle == 0)   /* initialize cycle steps for grids */
180             steps_grid_auto = RND(16) * (RND(2) ? -1 : +1);
181
182           if (IS_GRID_STEEL_AUTO(element) ||
183               IS_GRID_WOOD_AUTO(element))
184             game_mm.cycle[game_mm.num_cycle].steps = steps_grid_auto;
185           else
186             game_mm.cycle[game_mm.num_cycle].steps = RND(16) * (RND(2) ? -1 : +1);
187
188           game_mm.cycle[game_mm.num_cycle].x = x;
189           game_mm.cycle[game_mm.num_cycle].y = y;
190           game_mm.num_cycle++;
191         }
192
193         if (IS_BEAMER(element) || IS_FIBRE_OPTIC(element))
194         {
195           int beamer_nr = BEAMER_NR(element);
196           int nr = laser.beamer[beamer_nr][0].num;
197
198           laser.beamer[beamer_nr][nr].x = x;
199           laser.beamer[beamer_nr][nr].y = y;
200           laser.beamer[beamer_nr][nr].num = 1;
201         }
202       }
203       else if (IS_PACMAN(element))
204       {
205         InitMovDir_MM(x, y);
206       }
207       else if (IS_MCDUFFIN(element) || IS_LASER(element))
208       {
209         laser.start_edge.x = x;
210         laser.start_edge.y = y;
211         laser.start_angle = get_element_angle(element);
212       }
213
214       break;
215   }
216 }
217
218 static void InitCycleElements()
219 {
220   int i, j;
221
222   if (game_mm.num_cycle == 0)   /* no elements to cycle */
223     return;
224
225   for(i=0; i<16; i++)
226   {
227     for(j=0; j<game_mm.num_cycle; j++)
228     {
229       int x = game_mm.cycle[j].x;
230       int y = game_mm.cycle[j].y;
231       int step = SIGN(game_mm.cycle[j].steps);
232       int last_element = Feld[x][y];
233       int next_element = get_rotated_element(last_element, step);
234
235       if (!game_mm.cycle[j].steps)
236         continue;
237
238       Feld[x][y] = next_element;
239
240       DrawField_MM(x, y);
241       game_mm.cycle[j].steps -= step;
242     }
243
244     BackToFront();
245     ColorCycling();
246
247 #ifdef DEBUG
248     if (setup.quick_doors)
249       continue;
250 #endif
251
252     Delay(AUTO_ROTATE_DELAY);
253   }
254 }
255
256 static void InitLaser()
257 {
258   int start_element = Feld[laser.start_edge.x][laser.start_edge.y];
259   int step = (IS_LASER(start_element) ? 4 : 0);
260
261   LX = laser.start_edge.x * TILEX;
262   if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
263     LX += 14;
264   else
265     LX += (laser.start_angle == ANG_RAY_RIGHT ? 28 + step : 0 - step);
266
267   LY = laser.start_edge.y * TILEY;
268   if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
269     LY += (laser.start_angle == ANG_RAY_DOWN ? 28 + step : 0 - step);
270   else
271     LY += 14;
272
273   XS = 2 * Step[laser.start_angle].x;
274   YS = 2 * Step[laser.start_angle].y;
275
276   laser.current_angle = laser.start_angle;
277
278   laser.num_damages = 0;
279   laser.num_edges = 0;
280   laser.num_beamers = 0;
281   laser.beamer_edge[0] = 0;
282
283   AddLaserEdge(LX, LY);         /* set laser starting edge */
284
285   pen_ray = GetPixelFromRGB(window,
286                             native_mm_level.laser_red   * 0xFF,
287                             native_mm_level.laser_green * 0xFF,
288                             native_mm_level.laser_blue  * 0xFF);
289 }
290
291 void InitGameEngine_MM()
292 {
293   int i, x, y;
294
295   /* set global editor control values */
296   editor.draw_walls_masked = FALSE;
297
298   /* set global game control values */
299   game_mm.num_cycle = 0;
300   game_mm.num_pacman = 0;
301
302   game_mm.score = 0;
303   game_mm.energy_left = native_mm_level.time;
304   game_mm.kettles_still_needed =
305     (native_mm_level.auto_count_kettles ? 0 : native_mm_level.kettles_needed);
306   game_mm.lights_still_needed = 0;
307   game_mm.num_keys = 0;
308
309   game_mm.level_solved = FALSE;
310   game_mm.game_over = FALSE;
311   game_mm.game_over_cause = 0;
312
313   /* set global laser control values (must be set before "InitLaser()") */
314   laser.start_edge.x = 0;
315   laser.start_edge.y = 0;
316   laser.start_angle = 0;
317
318   for (i=0; i<MAX_NUM_BEAMERS; i++)
319     laser.beamer[i][0].num = laser.beamer[i][1].num = 0;
320
321   laser.overloaded = FALSE;
322   laser.overload_value = 0;
323   laser.fuse_off = FALSE;
324   laser.fuse_x = laser.fuse_y = -1;
325
326   laser.dest_element = EL_EMPTY;
327   laser.wall_mask = 0;
328
329   CT = Ct = 0;
330
331   for (x=0; x<lev_fieldx; x++)
332   {
333     for (y=0; y<lev_fieldy; y++)
334     {
335       Feld[x][y] = Ur[x][y];
336       Hit[x][y] = Box[x][y] = 0;
337       Angle[x][y] = 0;
338       MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
339       Store[x][y] = Store2[x][y] = 0;
340       Frame[x][y] = 0;
341       Stop[x][y] = FALSE;
342
343       InitField(x, y, TRUE);
344     }
345   }
346
347 #if 0
348   CloseDoor(DOOR_CLOSE_1);
349 #endif
350
351   DrawLevel_MM();
352 }
353
354 void InitGameEngine_MM_AfterFadingIn()
355 {
356   InitCycleElements();
357   InitLaser();
358
359 #if 0
360   /* copy default game door content to main double buffer */
361   BlitBitmap(pix[PIX_DOOR], drawto,
362              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
363 #endif
364
365 #if 0
366   DrawText(DX_LEVEL, DY_LEVEL,
367            int2str(level_nr, 2), FONT_TEXT_2);
368   DrawText(DX_KETTLES, DY_KETTLES,
369            int2str(game_mm.kettles_still_needed, 3), FONT_TEXT_2);
370   DrawText(DX_SCORE, DY_SCORE,
371            int2str(game_mm.score, 4), FONT_TEXT_2);
372 #endif
373
374 #if 0
375   UnmapGameButtons();
376   MapGameButtons();
377 #endif
378
379 #if 0
380   /* copy actual game door content to door double buffer for OpenDoor() */
381   BlitBitmap(drawto, pix[PIX_DB_DOOR],
382              DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
383 #endif
384
385 #if 0
386   OpenDoor(DOOR_OPEN_ALL);
387 #endif
388
389   if (setup.sound_loops)
390     PlaySoundExt(SND_FUEL, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT, SND_CTRL_PLAY_LOOP);
391
392 #if 0 // !!! TEMPORARILY DISABLED !!!
393   for(i=0; i<=game_mm.energy_left; i+=2)
394   {
395     if (!setup.sound_loops)
396       PlaySoundStereo(SND_FUEL, SOUND_MAX_RIGHT);
397
398 #if 0
399     BlitBitmap(pix[PIX_DOOR], drawto,
400                DOOR_GFX_PAGEX4 + XX_ENERGY,
401                DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
402                ENERGY_XSIZE, i,
403                DX_ENERGY, DY_ENERGY + ENERGY_YSIZE - i);
404 #endif
405
406     redraw_mask |= REDRAW_DOOR_1;
407     BackToFront();
408
409     ColorCycling();
410
411 #ifdef DEBUG
412     if (setup.quick_doors)
413       continue;
414 #endif
415
416     Delay(20);
417   }
418
419   if (setup.sound_loops)
420     StopSound(SND_FUEL);
421 #endif
422
423 #if 0
424   if (setup.sound_music && num_bg_loops)
425     PlayMusic(level_nr % num_bg_loops);
426 #endif
427
428   ScanLaser();
429 }
430
431 void AddLaserEdge(int lx, int ly)
432 {
433   if (lx < -2 || ly < -2 || lx >= SXSIZE + 2 || ly >= SYSIZE + 2)
434   {
435     Error(ERR_WARN, "AddLaserEdge: out of bounds: %d, %d", lx, ly);
436     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       if (game_mm.kettles_still_needed > 0)
1195         game_mm.kettles_still_needed--;
1196
1197       RaiseScore_MM(10);
1198
1199       if (game_mm.kettles_still_needed == 0)
1200       {
1201         int x, y;
1202         static int xy[4][2] =
1203         {
1204           { +1,  0 },
1205           {  0, -1 },
1206           { -1,  0 },
1207           {  0, +1 }
1208         };
1209
1210         PlaySoundStereo(SND_KLING, ST(ELX));
1211
1212         for(y=0; y<lev_fieldy; y++)
1213         {
1214           for(x=0; x<lev_fieldx; x++)
1215           {
1216             /* initiate opening animation of exit door */
1217             if (Feld[x][y] == EL_EXIT_CLOSED)
1218               Feld[x][y] = EL_EXIT_OPENING;
1219
1220             /* remove field that blocks receiver */
1221             if (IS_RECEIVER(Feld[x][y]))
1222             {
1223               int phase = Feld[x][y] - EL_RECEIVER_START;
1224               int blocking_x, blocking_y;
1225
1226               blocking_x = x + xy[phase][0];
1227               blocking_y = y + xy[phase][1];
1228
1229               if (IN_LEV_FIELD(blocking_x, blocking_y))
1230               {
1231                 Feld[blocking_x][blocking_y] = EL_EMPTY;
1232                 DrawField_MM(blocking_x, blocking_y);
1233               }
1234             }
1235           }
1236         }
1237
1238         DrawLaser(0, DL_LASER_ENABLED);
1239       }
1240     }
1241     else if (element == EL_KEY)
1242       game_mm.num_keys++;
1243     else if (element == EL_LIGHTBALL)
1244       RaiseScore_MM(10);
1245     else if (IS_PACMAN(element))
1246     {
1247       DeletePacMan(ELX, ELY);
1248       RaiseScore_MM(50);
1249     }
1250
1251     return FALSE;
1252   }
1253
1254   if (element == EL_LIGHTBULB_OFF || element == EL_LIGHTBULB_ON)
1255   {
1256     PlaySoundStereo(SND_KINK, ST(ELX));
1257
1258     DrawLaser(0, DL_LASER_ENABLED);
1259
1260     if (Feld[ELX][ELY] == EL_LIGHTBULB_OFF)
1261     {
1262       Feld[ELX][ELY] = EL_LIGHTBULB_ON;
1263       game_mm.lights_still_needed--;
1264     }
1265     else
1266     {
1267       Feld[ELX][ELY] = EL_LIGHTBULB_OFF;
1268       game_mm.lights_still_needed++;
1269     }
1270
1271     DrawField_MM(ELX, ELY);
1272     DrawLaser(0, DL_LASER_ENABLED);
1273
1274     /*
1275     BackToFront();
1276     */
1277     laser.stops_inside_element = TRUE;
1278
1279     return TRUE;
1280   }
1281
1282 #if 0
1283   printf("HitElement (4): element == %d\n", element);
1284 #endif
1285
1286   if ((IS_BEAMER(element) || IS_FIBRE_OPTIC(element)) &&
1287       laser.num_beamers < MAX_NUM_BEAMERS &&
1288       laser.beamer[BEAMER_NR(element)][1].num)
1289   {
1290     int beamer_angle = get_element_angle(element);
1291     int beamer_nr = BEAMER_NR(element);
1292     int step_size;
1293
1294 #if 0
1295   printf("HitElement (BEAMER): element == %d\n", element);
1296 #endif
1297
1298     laser.num_damages--;
1299
1300     if (IS_FIBRE_OPTIC(element) ||
1301         laser.current_angle == get_opposite_angle(beamer_angle))
1302     {
1303       int pos;
1304
1305       LX = ELX * TILEX + 14;
1306       LY = ELY * TILEY + 14;
1307       AddLaserEdge(LX, LY);
1308       AddDamagedField(ELX, ELY);
1309       laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1310
1311       if (!Hit[ELX][ELY])
1312         Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1313
1314       pos = (ELX == laser.beamer[beamer_nr][0].x &&
1315              ELY == laser.beamer[beamer_nr][0].y ? 1 : 0);
1316       ELX = laser.beamer[beamer_nr][pos].x;
1317       ELY = laser.beamer[beamer_nr][pos].y;
1318       LX = ELX * TILEX + 14;
1319       LY = ELY * TILEY + 14;
1320
1321       if (IS_BEAMER(element))
1322       {
1323         laser.current_angle = get_element_angle(Feld[ELX][ELY]);
1324         XS = 2 * Step[laser.current_angle].x;
1325         YS = 2 * Step[laser.current_angle].y;
1326       }
1327
1328       laser.beamer_edge[laser.num_beamers] = laser.num_edges;
1329       AddLaserEdge(LX, LY);
1330       AddDamagedField(ELX, ELY);
1331       laser.damage[laser.num_damages - 1].is_mirror = TRUE;
1332
1333       if (!Hit[ELX][ELY])
1334         Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1335
1336       if (laser.current_angle == (laser.current_angle >> 1) << 1)
1337         step_size = 8;
1338       else
1339         step_size = 4;
1340
1341       LX += step_size * XS;
1342       LY += step_size * YS;
1343
1344       laser.num_beamers++;
1345
1346       return FALSE;
1347     }
1348   }
1349
1350   return TRUE;
1351 }
1352
1353 boolean HitOnlyAnEdge(int element, int hit_mask)
1354 {
1355   /* check if the laser hit only the edge of an element and, if so, go on */
1356
1357 #if 0
1358   printf("LX, LY, hit_mask == %d, %d, %d\n", LX, LY, hit_mask);
1359 #endif
1360
1361   if ((hit_mask == HIT_MASK_TOPLEFT ||
1362        hit_mask == HIT_MASK_TOPRIGHT ||
1363        hit_mask == HIT_MASK_BOTTOMLEFT ||
1364        hit_mask == HIT_MASK_BOTTOMRIGHT) &&
1365       laser.current_angle % 4)                  /* angle is not 90° */
1366   {
1367     int dx, dy;
1368
1369     if (hit_mask == HIT_MASK_TOPLEFT)
1370     {
1371       dx = -1;
1372       dy = -1;
1373     }
1374     else if (hit_mask == HIT_MASK_TOPRIGHT)
1375     {
1376       dx = +1;
1377       dy = -1;
1378     }
1379     else if (hit_mask == HIT_MASK_BOTTOMLEFT)
1380     {
1381       dx = -1;
1382       dy = +1;
1383     }
1384     else /* (hit_mask == HIT_MASK_BOTTOMRIGHT) */
1385     {
1386       dx = +1;
1387       dy = +1;
1388     }
1389
1390     AddDamagedField((LX + 2 * dx) / TILEX, (LY + 2 * dy) / TILEY);
1391     LX += XS;
1392     LY += YS;
1393
1394 #if 0
1395     printf("[HitOnlyAnEdge() == TRUE]\n");
1396 #endif
1397
1398     return TRUE;
1399   }
1400
1401 #if 0
1402     printf("[HitOnlyAnEdge() == FALSE]\n");
1403 #endif
1404
1405   return FALSE;
1406 }
1407
1408 boolean HitPolarizer(int element, int hit_mask)
1409 {
1410   if (HitOnlyAnEdge(element, hit_mask))
1411     return FALSE;
1412
1413   if (IS_DF_GRID(element))
1414   {
1415     int grid_angle = get_element_angle(element);
1416
1417 #if 0
1418     printf("HitPolarizer: angle: grid == %d, laser == %d\n",
1419            grid_angle, laser.current_angle);
1420 #endif
1421
1422     AddLaserEdge(LX, LY);
1423     AddDamagedField(ELX, ELY);
1424
1425     if (!Hit[ELX][ELY])
1426       Hit[ELX][ELY] = laser.damage[laser.num_damages - 1].edge;
1427
1428     if (laser.current_angle == grid_angle ||
1429         laser.current_angle == get_opposite_angle(grid_angle))
1430     {
1431       /* skip the whole element before continuing the scan */
1432       do
1433       {
1434         LX += XS;
1435         LY += YS;
1436       }
1437       while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
1438
1439       if (LX/TILEX > ELX || LY/TILEY > ELY)
1440       {
1441         /* skipping scan positions to the right and down skips one scan
1442            position too much, because this is only the top left scan position
1443            of totally four scan positions (plus one to the right, one to the
1444            bottom and one to the bottom right) */
1445
1446         LX -= XS;
1447         LY -= YS;
1448       }
1449
1450       AddLaserEdge(LX, LY);
1451
1452       LX += XS;
1453       LY += YS;
1454
1455 #if 0
1456       printf("HitPolarizer: LX, LY == %d, %d [%d, %d] [%d, %d]\n",
1457              LX, LY,
1458              LX / TILEX, LY / TILEY,
1459              LX % TILEX, LY % TILEY);
1460 #endif
1461
1462       return FALSE;
1463     }
1464     else if (IS_GRID_STEEL_FIXED(element) || IS_GRID_STEEL_AUTO(element))
1465       return HitReflectingWalls(element, hit_mask);
1466     else
1467       return HitAbsorbingWalls(element, hit_mask);
1468   }
1469   else if (IS_GRID_STEEL(element))
1470     return HitReflectingWalls(element, hit_mask);
1471   else  /* IS_GRID_WOOD */
1472     return HitAbsorbingWalls(element, hit_mask);
1473
1474   return TRUE;
1475 }
1476
1477 boolean HitBlock(int element, int hit_mask)
1478 {
1479   boolean check = FALSE;
1480
1481   if ((element == EL_GATE_STONE || element == EL_GATE_WOOD) &&
1482       game_mm.num_keys == 0)
1483     check = TRUE;
1484
1485   if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1486   {
1487     int i, x, y;
1488     int ex = ELX * TILEX + 14;
1489     int ey = ELY * TILEY + 14;
1490
1491     check = TRUE;
1492
1493     for(i=1; i<32; i++)
1494     {
1495       x = LX + i * XS;
1496       y = LY + i * YS;
1497
1498       if ((x == ex || x == ex + 1) && (y == ey || y == ey + 1))
1499         check = FALSE;
1500     }
1501   }
1502
1503   if (check && (element == EL_BLOCK_WOOD || element == EL_GATE_WOOD))
1504     return HitAbsorbingWalls(element, hit_mask);
1505
1506   if (check)
1507   {
1508     AddLaserEdge(LX - XS, LY - YS);
1509     AddDamagedField(ELX, ELY);
1510
1511     if (!Box[ELX][ELY])
1512       Box[ELX][ELY] = laser.num_edges;
1513
1514     return HitReflectingWalls(element, hit_mask);
1515   }
1516
1517   if (element == EL_GATE_STONE || element == EL_GATE_WOOD)
1518   {
1519     int xs = XS / 2, ys = YS / 2;
1520     int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1521     int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1522
1523     if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1524         (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1525     {
1526       laser.overloaded = (element == EL_GATE_STONE);
1527       return TRUE;
1528     }
1529
1530     if (ABS(xs) == 1 && ABS(ys) == 1 &&
1531         (hit_mask == HIT_MASK_TOP ||
1532          hit_mask == HIT_MASK_LEFT ||
1533          hit_mask == HIT_MASK_RIGHT ||
1534          hit_mask == HIT_MASK_BOTTOM))
1535       AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1536                                   hit_mask == HIT_MASK_BOTTOM),
1537                       ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1538                                   hit_mask == HIT_MASK_RIGHT));
1539     AddLaserEdge(LX, LY);
1540
1541     Bang_MM(ELX, ELY);
1542
1543     game_mm.num_keys--;
1544     if (element == EL_GATE_STONE && Box[ELX][ELY])
1545     {
1546       DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
1547       /*
1548       BackToFront();
1549       */
1550       ScanLaser();
1551
1552       return TRUE;
1553     }
1554
1555     return FALSE;
1556   }
1557
1558   if (element == EL_BLOCK_STONE || element == EL_BLOCK_WOOD)
1559   {
1560     int xs = XS / 2, ys = YS / 2;
1561     int hit_mask_diagonal1 = HIT_MASK_TOPRIGHT | HIT_MASK_BOTTOMLEFT;
1562     int hit_mask_diagonal2 = HIT_MASK_TOPLEFT | HIT_MASK_BOTTOMRIGHT;
1563
1564     if ((hit_mask & hit_mask_diagonal1) == hit_mask_diagonal1 ||
1565         (hit_mask & hit_mask_diagonal2) == hit_mask_diagonal2)
1566     {
1567       laser.overloaded = (element == EL_BLOCK_STONE);
1568
1569       return TRUE;
1570     }
1571
1572     if (ABS(xs) == 1 && ABS(ys) == 1 &&
1573         (hit_mask == HIT_MASK_TOP ||
1574          hit_mask == HIT_MASK_LEFT ||
1575          hit_mask == HIT_MASK_RIGHT ||
1576          hit_mask == HIT_MASK_BOTTOM))
1577       AddDamagedField(ELX - xs * (hit_mask == HIT_MASK_TOP ||
1578                                   hit_mask == HIT_MASK_BOTTOM),
1579                       ELY - ys * (hit_mask == HIT_MASK_LEFT ||
1580                                   hit_mask == HIT_MASK_RIGHT));
1581     AddDamagedField(ELX, ELY);
1582
1583     LX = ELX * TILEX + 14;
1584     LY = ELY * TILEY + 14;
1585     AddLaserEdge(LX, LY);
1586
1587     laser.stops_inside_element = TRUE;
1588
1589     return TRUE;
1590   }
1591
1592   return TRUE;
1593 }
1594
1595 boolean HitLaserSource(int element, int hit_mask)
1596 {
1597    if (HitOnlyAnEdge(element, hit_mask))
1598      return FALSE;
1599
1600    PlaySoundStereo(SND_AUTSCH, ST(ELX));
1601    laser.overloaded = TRUE;
1602
1603    return TRUE;
1604 }
1605
1606 boolean HitLaserDestination(int element, int hit_mask)
1607 {
1608   if (HitOnlyAnEdge(element, hit_mask))
1609     return FALSE;
1610
1611   if (element != EL_EXIT_OPEN &&
1612       !(IS_RECEIVER(element) &&
1613         game_mm.kettles_still_needed == 0 &&
1614         laser.current_angle == get_opposite_angle(get_element_angle(element))))
1615   {
1616     PlaySoundStereo(SND_HOLZ, ST(ELX));
1617     return TRUE;
1618   }
1619
1620   if (IS_RECEIVER(element) ||
1621       (IS_22_5_ANGLE(laser.current_angle) &&
1622        (ELX != (LX + 6 * XS) / TILEX ||
1623         ELY != (LY + 6 * YS) / TILEY ||
1624         LX + 6 * XS < 0 ||
1625         LY + 6 * YS < 0)))
1626   {
1627     LX -= XS;
1628     LY -= YS;
1629   }
1630   else
1631   {
1632     LX = ELX * TILEX + 14;
1633     LY = ELY * TILEY + 14;
1634
1635     laser.stops_inside_element = TRUE;
1636   }
1637
1638   AddLaserEdge(LX, LY);
1639   AddDamagedField(ELX, ELY);
1640
1641   if (game_mm.lights_still_needed == 0)
1642     game_mm.level_solved = TRUE;
1643
1644   return TRUE;
1645 }
1646
1647 boolean HitReflectingWalls(int element, int hit_mask)
1648 {
1649   /* check if laser hits side of a wall with an angle that is not 90° */
1650   if (!IS_90_ANGLE(laser.current_angle) && (hit_mask == HIT_MASK_TOP ||
1651                                             hit_mask == HIT_MASK_LEFT ||
1652                                             hit_mask == HIT_MASK_RIGHT ||
1653                                             hit_mask == HIT_MASK_BOTTOM))
1654   {
1655     PlaySoundStereo(SND_HUI, ST(ELX));
1656     LX -= XS;
1657     LY -= YS;
1658     if (!IS_DF_GRID(element))
1659       AddLaserEdge(LX, LY);
1660
1661     /* check if laser hits wall with an angle of 45° */
1662     if (!IS_22_5_ANGLE(laser.current_angle))
1663     {
1664       if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1665       {
1666         LX += 2 * XS;
1667         laser.current_angle = get_mirrored_angle(laser.current_angle,
1668                                                  ANG_MIRROR_0);
1669       }
1670       else      /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1671       {
1672         LY += 2 * YS;
1673         laser.current_angle = get_mirrored_angle(laser.current_angle,
1674                                                  ANG_MIRROR_90);
1675       }
1676
1677       AddLaserEdge(LX, LY);
1678       XS = 2 * Step[laser.current_angle].x;
1679       YS = 2 * Step[laser.current_angle].y;
1680
1681       return FALSE;
1682     }
1683     else if (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM)
1684     {
1685       laser.current_angle = get_mirrored_angle(laser.current_angle,
1686                                                ANG_MIRROR_0);
1687       if (ABS(XS) == 4)
1688       {
1689         LX += 2 * XS;
1690         if (!IS_DF_GRID(element))
1691           AddLaserEdge(LX, LY);
1692       }
1693       else
1694       {
1695         LX += XS;
1696         if (!IS_DF_GRID(element))
1697           AddLaserEdge(LX, LY + YS / 2);
1698
1699         LX += XS;
1700         if (!IS_DF_GRID(element))
1701           AddLaserEdge(LX, LY);
1702       }
1703
1704       YS = 2 * Step[laser.current_angle].y;
1705
1706       return FALSE;
1707     }
1708     else        /* hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT */
1709     {
1710       laser.current_angle = get_mirrored_angle(laser.current_angle,
1711                                                ANG_MIRROR_90);
1712       if (ABS(YS) == 4)
1713       {
1714         LY += 2 * YS;
1715         if (!IS_DF_GRID(element))
1716           AddLaserEdge(LX, LY);
1717       }
1718       else
1719       {
1720         LY += YS;
1721         if (!IS_DF_GRID(element))
1722           AddLaserEdge(LX + XS / 2, LY);
1723
1724         LY += YS;
1725         if (!IS_DF_GRID(element))
1726           AddLaserEdge(LX, LY);
1727       }
1728
1729       XS = 2 * Step[laser.current_angle].x;
1730
1731       return FALSE;
1732     }
1733   }
1734
1735   /* reflection at the edge of reflecting DF style wall */
1736   if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1737   {
1738     if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1739          hit_mask == HIT_MASK_TOPRIGHT) ||
1740         ((laser.current_angle == 5 || laser.current_angle == 7) &&
1741          hit_mask == HIT_MASK_TOPLEFT) ||
1742         ((laser.current_angle == 9 || laser.current_angle == 11) &&
1743          hit_mask == HIT_MASK_BOTTOMLEFT) ||
1744         ((laser.current_angle == 13 || laser.current_angle == 15) &&
1745          hit_mask == HIT_MASK_BOTTOMRIGHT))
1746     {
1747       int mirror_angle =
1748         (hit_mask == HIT_MASK_TOPRIGHT || hit_mask == HIT_MASK_BOTTOMLEFT ?
1749          ANG_MIRROR_135 : ANG_MIRROR_45);
1750
1751       PlaySoundStereo(SND_HUI, ST(ELX));
1752       AddDamagedField(ELX, ELY);
1753       AddLaserEdge(LX, LY);
1754
1755       laser.current_angle = get_mirrored_angle(laser.current_angle,
1756                                                mirror_angle);
1757       XS = 8 / -XS;
1758       YS = 8 / -YS;
1759
1760       LX += XS;
1761       LY += YS;
1762       AddLaserEdge(LX, LY);
1763
1764       return FALSE;
1765     }
1766   }
1767
1768   /* reflection inside an edge of reflecting DF style wall */
1769   if (IS_DF_WALL_STEEL(element) && IS_22_5_ANGLE(laser.current_angle))
1770   {
1771     if (((laser.current_angle == 1 || laser.current_angle == 3) &&
1772          hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT)) ||
1773         ((laser.current_angle == 5 || laser.current_angle == 7) &&
1774          hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMRIGHT)) ||
1775         ((laser.current_angle == 9 || laser.current_angle == 11) &&
1776          hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT)) ||
1777         ((laser.current_angle == 13 || laser.current_angle == 15) &&
1778          hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPLEFT)))
1779     {
1780       int mirror_angle =
1781         (hit_mask == (HIT_MASK_ALL ^ HIT_MASK_BOTTOMLEFT) ||
1782          hit_mask == (HIT_MASK_ALL ^ HIT_MASK_TOPRIGHT) ?
1783          ANG_MIRROR_135 : ANG_MIRROR_45);
1784
1785       PlaySoundStereo(SND_HUI, ST(ELX));
1786       /*
1787       AddDamagedField(ELX, ELY);
1788       */
1789       AddLaserEdge(LX - XS, LY - YS);
1790       AddLaserEdge(LX - XS + (ABS(XS) == 4 ? XS/2 : 0),
1791                    LY - YS + (ABS(YS) == 4 ? YS/2 : 0));
1792
1793       laser.current_angle = get_mirrored_angle(laser.current_angle,
1794                                                mirror_angle);
1795       XS = 8 / -XS;
1796       YS = 8 / -YS;
1797
1798       LX += XS;
1799       LY += YS;
1800       AddLaserEdge(LX, LY);
1801
1802       return FALSE;
1803     }
1804   }
1805
1806   /* check if laser hits DF style wall with an angle of 90° */
1807   if (IS_DF_WALL(element) && IS_90_ANGLE(laser.current_angle))
1808   {
1809     if ((IS_HORIZ_ANGLE(laser.current_angle) &&
1810          (!(hit_mask & HIT_MASK_TOP) || !(hit_mask & HIT_MASK_BOTTOM))) ||
1811         (IS_VERT_ANGLE(laser.current_angle) &&
1812          (!(hit_mask & HIT_MASK_LEFT) || !(hit_mask & HIT_MASK_RIGHT))))
1813     {
1814       static int last_LX = 0, last_LY = 0, last_hit_mask = 0;
1815
1816       /* laser at last step touched nothing or the same side of the wall */
1817       if (LX != last_LX || LY != last_LY || hit_mask == last_hit_mask)
1818       {
1819         AddDamagedField(ELX, ELY);
1820         LX += 8 * XS;
1821         LY += 8 * YS;
1822
1823         last_LX = LX;
1824         last_LY = LY;
1825         last_hit_mask = hit_mask;
1826
1827         return FALSE;
1828       }
1829     }
1830   }
1831
1832   if (!HitOnlyAnEdge(element, hit_mask))
1833   {
1834     laser.overloaded = TRUE;
1835     return TRUE;
1836   }
1837
1838   return FALSE;
1839 }
1840
1841 boolean HitAbsorbingWalls(int element, int hit_mask)
1842 {
1843   if (HitOnlyAnEdge(element, hit_mask))
1844     return FALSE;
1845
1846   if (ABS(XS) == 4 &&
1847       (hit_mask == HIT_MASK_LEFT || hit_mask == HIT_MASK_RIGHT))
1848   {
1849     AddLaserEdge(LX - XS, LY - YS);
1850     LX = LX + XS / 2;
1851     LY = LY + YS;
1852   }
1853
1854   if (ABS(YS) == 4 &&
1855       (hit_mask == HIT_MASK_TOP || hit_mask == HIT_MASK_BOTTOM))
1856   {
1857     AddLaserEdge(LX - XS, LY - YS);
1858     LX = LX + XS;
1859     LY = LY + YS / 2;
1860   }
1861
1862   if (IS_WALL_WOOD(element) ||
1863       IS_DF_WALL_WOOD(element) ||
1864       IS_GRID_WOOD(element) ||
1865       IS_GRID_WOOD_FIXED(element) ||
1866       IS_GRID_WOOD_AUTO(element) ||
1867       element == EL_FUSE_ON ||
1868       element == EL_BLOCK_WOOD ||
1869       element == EL_GATE_WOOD)
1870   {
1871     PlaySoundStereo(SND_HOLZ, ST(ELX));
1872     return TRUE;
1873   }
1874
1875   if (IS_WALL_ICE(element))
1876   {
1877     int mask;
1878
1879     mask = (LX + XS) / MINI_TILEX - ELX * 2 + 1;    /* Quadrant (horizontal) */
1880     mask <<= (((LY + YS) / MINI_TILEY - ELY * 2) > 0) * 2;  /* || (vertical) */
1881
1882     /* check if laser hits wall with an angle of 90° */
1883     if (IS_90_ANGLE(laser.current_angle))
1884       mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1885
1886     if (mask == 1 || mask == 2 || mask == 4 || mask == 8)
1887     {
1888       int i;
1889
1890       for(i=0; i<4; i++)
1891       {
1892         if (mask == (1 << i) && (XS > 0) == (i % 2) && (YS > 0) == (i / 2))
1893           mask = 15 - (8 >> i);
1894         else if (ABS(XS) == 4 &&
1895                  mask == (1 << i) &&
1896                  (XS > 0) == (i % 2) &&
1897                  (YS < 0) == (i / 2))
1898           mask = 3 + (i / 2) * 9;
1899         else if (ABS(YS) == 4 &&
1900                  mask == (1 << i) &&
1901                  (XS < 0) == (i % 2) &&
1902                  (YS > 0) == (i / 2))
1903           mask = 5 + (i % 2) * 5;
1904       }
1905     }
1906
1907     laser.wall_mask = mask;
1908   }
1909   else if (IS_WALL_AMOEBA(element))
1910   {
1911     int elx = (LX - 2 * XS) / TILEX;
1912     int ely = (LY - 2 * YS) / TILEY;
1913     int element2 = Feld[elx][ely];
1914     int mask;
1915
1916     if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element2))
1917     {
1918       laser.dest_element = EL_EMPTY;
1919       return TRUE;
1920     }
1921
1922     ELX = elx;
1923     ELY = ely;
1924
1925     mask = (LX - 2 * XS) / 16 - ELX * 2 + 1;
1926     mask <<= ((LY - 2 * YS) / 16 - ELY * 2) * 2;
1927
1928     if (IS_90_ANGLE(laser.current_angle))
1929       mask += mask * (2 + IS_HORIZ_ANGLE(laser.current_angle) * 2);
1930
1931     laser.dest_element = element2 | EL_WALL_AMOEBA;
1932
1933     laser.wall_mask = mask;
1934   }
1935
1936   return TRUE;
1937 }
1938
1939 void OpenExit(int x, int y)
1940 {
1941   int delay = 6;
1942
1943   if (!MovDelay[x][y])          /* next animation frame */
1944     MovDelay[x][y] = 4 * delay;
1945
1946   if (MovDelay[x][y])           /* wait some time before next frame */
1947   {
1948     int phase;
1949
1950     MovDelay[x][y]--;
1951     phase = MovDelay[x][y] / delay;
1952     if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
1953       DrawGraphicAnimation_MM(x, y, IMG_MM_EXIT_OPENING, 3 - phase);
1954
1955     if (!MovDelay[x][y])
1956     {
1957       Feld[x][y] = EL_EXIT_OPEN;
1958       DrawField_MM(x, y);
1959     }
1960   }
1961 }
1962
1963 void OpenSurpriseBall(int x, int y)
1964 {
1965   int delay = 2;
1966
1967   if (!MovDelay[x][y])          /* next animation frame */
1968     MovDelay[x][y] = 50 * delay;
1969
1970   if (MovDelay[x][y])           /* wait some time before next frame */
1971   {
1972     MovDelay[x][y]--;
1973     if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
1974     {
1975       Bitmap *bitmap;
1976       int graphic = el2gfx(Store[x][y]);
1977       int gx, gy;
1978       int dx = RND(26), dy = RND(26);
1979
1980       getGraphicSource(graphic, 0, &bitmap, &gx, &gy);
1981       BlitBitmap(bitmap, drawto, gx + dx, gy + dy, 6, 6,
1982                  SX + x * TILEX + dx, SY + y * TILEY + dy);
1983       MarkTileDirty(x, y);
1984     }
1985
1986     if (!MovDelay[x][y])
1987     {
1988       Feld[x][y] = Store[x][y];
1989       Store[x][y] = 0;
1990       DrawField_MM(x, y);
1991
1992       ScanLaser();
1993     }
1994   }
1995 }
1996
1997 void MeltIce(int x, int y)
1998 {
1999   int frames = 5;
2000   int delay = 5;
2001
2002   if (!MovDelay[x][y])          /* next animation frame */
2003     MovDelay[x][y] = frames * delay;
2004
2005   if (MovDelay[x][y])           /* wait some time before next frame */
2006   {
2007     int phase;
2008     int wall_mask = Store2[x][y];
2009     int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_ICE;
2010
2011     MovDelay[x][y]--;
2012     phase = frames - MovDelay[x][y] / delay - 1;
2013
2014     if (!MovDelay[x][y])
2015     {
2016       int i;
2017
2018       Feld[x][y] = real_element & (wall_mask ^ 0xFF);
2019       Store[x][y] = Store2[x][y] = 0;
2020
2021       DrawWalls_MM(x, y, Feld[x][y]);
2022
2023       if (Feld[x][y] == EL_WALL_ICE)
2024         Feld[x][y] = EL_EMPTY;
2025
2026       for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
2027         if (laser.damage[i].is_mirror)
2028           break;
2029
2030       if (i > 0)
2031         DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
2032       else
2033         DrawLaser(0, DL_LASER_DISABLED);
2034
2035       ScanLaser();
2036     }
2037     else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2038     {
2039       DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2040
2041       laser.redraw = TRUE;
2042     }
2043   }
2044 }
2045
2046 void GrowAmoeba(int x, int y)
2047 {
2048   int frames = 5;
2049   int delay = 1;
2050
2051   if (!MovDelay[x][y])          /* next animation frame */
2052     MovDelay[x][y] = frames * delay;
2053
2054   if (MovDelay[x][y])           /* wait some time before next frame */
2055   {
2056     int phase;
2057     int wall_mask = Store2[x][y];
2058     int real_element = Feld[x][y] - EL_WALL_CHANGING + EL_WALL_AMOEBA;
2059
2060     MovDelay[x][y]--;
2061     phase = MovDelay[x][y] / delay;
2062
2063     if (!MovDelay[x][y])
2064     {
2065       Feld[x][y] = real_element;
2066       Store[x][y] = Store2[x][y] = 0;
2067
2068       DrawWalls_MM(x, y, Feld[x][y]);
2069       DrawLaser(0, DL_LASER_ENABLED);
2070     }
2071     else if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
2072       DrawWallsAnimation_MM(x, y, real_element, phase, wall_mask);
2073   }
2074 }
2075
2076 static void Explode_MM(int x, int y, int phase, int mode)
2077 {
2078   int num_phase = 9, delay = 2;
2079   int last_phase = num_phase * delay;
2080   int half_phase = (num_phase / 2) * delay;
2081
2082   laser.redraw = TRUE;
2083
2084   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
2085   {
2086     int center_element = Feld[x][y];
2087
2088     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2089     {
2090       /* put moving element to center field (and let it explode there) */
2091       center_element = MovingOrBlocked2Element_MM(x, y);
2092       RemoveMovingField_MM(x, y);
2093       Feld[x][y] = center_element;
2094     }
2095
2096     if (center_element == EL_BOMB || IS_MCDUFFIN(center_element))
2097       Store[x][y] = center_element;
2098     else
2099       Store[x][y] = EL_EMPTY;
2100     Store2[x][y] = mode;
2101     Feld[x][y] = EL_EXPLODING_OPAQUE;
2102     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2103     Frame[x][y] = 1;
2104
2105     return;
2106   }
2107
2108   Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
2109
2110   if (phase == half_phase)
2111   {
2112     Feld[x][y] = EL_EXPLODING_TRANSP;
2113
2114     if (x == ELX && y == ELY)
2115       ScanLaser();
2116   }
2117
2118   if (phase == last_phase)
2119   {
2120     if (Store[x][y] == EL_BOMB)
2121     {
2122       laser.num_damages--;
2123       DrawLaser(0, DL_LASER_DISABLED);
2124       laser.num_edges = 0;
2125
2126       Bang_MM(laser.start_edge.x, laser.start_edge.y);
2127       Store[x][y] = EL_EMPTY;
2128     }
2129     else if (IS_MCDUFFIN(Store[x][y]))
2130     {
2131       game_mm.game_over = TRUE;
2132       game_mm.game_over_cause = GAME_OVER_BOMB;
2133       Store[x][y] = EL_EMPTY;
2134     }
2135
2136     Feld[x][y] = Store[x][y];
2137     Store[x][y] = Store2[x][y] = 0;
2138     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2139     InitField(x, y, FALSE);
2140     DrawField_MM(x, y);
2141   }
2142   else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2143   {
2144     int graphic = IMG_MM_DEFAULT_EXPLODING;
2145     int graphic_phase = (phase / delay - 1);
2146     Bitmap *bitmap;
2147     int src_x, src_y;
2148
2149     if (Store2[x][y] == EX_KETTLE)
2150     {
2151       if (graphic_phase < 3)
2152         graphic = IMG_MM_KETTLE_EXPLODING;
2153       else if (graphic_phase < 5)
2154       {
2155         graphic_phase += 3;
2156       }
2157       else
2158       {
2159         graphic = IMG_EMPTY;
2160         graphic_phase = 0;
2161       }
2162     }
2163     else if (Store2[x][y] == EX_SHORT)
2164     {
2165       if (graphic_phase < 4)
2166         graphic_phase += 4;
2167       else
2168       {
2169         graphic = IMG_EMPTY;
2170         graphic_phase = 0;
2171       }
2172     }
2173
2174     getGraphicSource(graphic, graphic_phase, &bitmap, &src_x, &src_y);
2175
2176     BlitBitmap(bitmap, drawto_field, src_x, src_y, TILEX, TILEY,
2177                FX + x * TILEX, FY + y * TILEY);
2178     MarkTileDirty(x, y);
2179   }
2180 }
2181
2182 static void Bang_MM(int x, int y)
2183 {
2184   int element = Feld[x][y];
2185   int mode = EX_NORMAL;
2186
2187 #if 0
2188   DrawLaser(0, DL_LASER_ENABLED);
2189 #endif
2190
2191   switch(element)
2192   {
2193     case EL_KETTLE:
2194       mode = EX_KETTLE;
2195       break;
2196
2197     case EL_GATE_STONE:
2198     case EL_GATE_WOOD:
2199       mode = EX_SHORT;
2200       break;
2201
2202     default:
2203       mode = EX_NORMAL;
2204       break;
2205   }
2206
2207   if (IS_PACMAN(element))
2208     PlaySoundStereo(SND_QUIEK, ST(x));
2209   else if (element == EL_BOMB || IS_MCDUFFIN(element))
2210     PlaySoundStereo(SND_ROAAAR, ST(x));
2211   else if (element == EL_KEY)
2212     PlaySoundStereo(SND_KLING, ST(x));
2213   else
2214     PlaySoundStereo((mode == EX_SHORT ? SND_WHOOSH : SND_KABUMM), ST(x));
2215
2216   Explode_MM(x, y, EX_PHASE_START, mode);
2217 }
2218
2219 void TurnRound(int x, int y)
2220 {
2221   static struct
2222   {
2223     int x, y;
2224   } move_xy[] =
2225   {
2226     { 0, 0 },
2227     {-1, 0 },
2228     {+1, 0 },
2229     { 0, 0 },
2230     { 0, -1 },
2231     { 0, 0 }, { 0, 0 }, { 0, 0 },
2232     { 0, +1 }
2233   };
2234   static struct
2235   {
2236     int left, right, back;
2237   } turn[] =
2238   {
2239     { 0,        0,              0 },
2240     { MV_DOWN,  MV_UP,          MV_RIGHT },
2241     { MV_UP,    MV_DOWN,        MV_LEFT },
2242     { 0,        0,              0 },
2243     { MV_LEFT,  MV_RIGHT,       MV_DOWN },
2244     { 0,0,0 },  { 0,0,0 },      { 0,0,0 },
2245     { MV_RIGHT, MV_LEFT,        MV_UP }
2246   };
2247
2248   int element = Feld[x][y];
2249   int old_move_dir = MovDir[x][y];
2250   int right_dir = turn[old_move_dir].right;
2251   int back_dir = turn[old_move_dir].back;
2252   int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
2253   int right_x = x+right_dx, right_y = y+right_dy;
2254
2255   if (element == EL_PACMAN)
2256   {
2257     boolean can_turn_right = FALSE;
2258
2259     if (IN_LEV_FIELD(right_x, right_y) &&
2260         IS_EATABLE4PACMAN(Feld[right_x][right_y]))
2261       can_turn_right = TRUE;
2262
2263     if (can_turn_right)
2264       MovDir[x][y] = right_dir;
2265     else
2266       MovDir[x][y] = back_dir;
2267
2268     MovDelay[x][y] = 0;
2269   }
2270 }
2271
2272 static void StartMoving_MM(int x, int y)
2273 {
2274   int element = Feld[x][y];
2275
2276   if (Stop[x][y])
2277     return;
2278
2279   if (CAN_MOVE(element))
2280   {
2281     int newx, newy;
2282
2283     if (MovDelay[x][y])         /* wait some time before next movement */
2284     {
2285       MovDelay[x][y]--;
2286
2287       if (MovDelay[x][y])
2288         return;
2289     }
2290
2291     /* now make next step */
2292
2293     Moving2Blocked_MM(x, y, &newx, &newy);      /* get next screen position */
2294
2295     if (element == EL_PACMAN &&
2296         IN_LEV_FIELD(newx, newy) && IS_EATABLE4PACMAN(Feld[newx][newy]) &&
2297         !ObjHit(newx, newy, HIT_POS_CENTER))
2298     {
2299       Store[newx][newy] = Feld[newx][newy];
2300       Feld[newx][newy] = EL_EMPTY;
2301       DrawField_MM(newx, newy);
2302     }
2303     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy) ||
2304              ObjHit(newx, newy, HIT_POS_CENTER))
2305     {
2306       /* object was running against a wall */
2307
2308       TurnRound(x, y);
2309
2310       return;
2311     }
2312
2313     InitMovingField_MM(x, y, MovDir[x][y]);
2314   }
2315
2316   if (MovDir[x][y])
2317     ContinueMoving_MM(x, y);
2318 }
2319
2320 static void ContinueMoving_MM(int x, int y)
2321 {
2322   int element = Feld[x][y];
2323   int direction = MovDir[x][y];
2324   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2325   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
2326   int horiz_move = (dx!=0);
2327   int newx = x + dx, newy = y + dy;
2328   int step = (horiz_move ? dx : dy) * TILEX / 8;
2329
2330   MovPos[x][y] += step;
2331
2332   if (ABS(MovPos[x][y]) >= TILEX)       /* object reached its destination */
2333   {
2334     Feld[x][y] = EL_EMPTY;
2335     Feld[newx][newy] = element;
2336
2337     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2338     MovDelay[newx][newy] = 0;
2339
2340     if (!CAN_MOVE(element))
2341       MovDir[newx][newy] = 0;
2342
2343     DrawField_MM(x, y);
2344     DrawField_MM(newx, newy);
2345
2346     Stop[newx][newy] = TRUE;
2347
2348     if (element == EL_PACMAN)
2349     {
2350       if (Store[newx][newy] == EL_BOMB)
2351         Bang_MM(newx, newy);
2352
2353       if (IS_WALL_AMOEBA(Store[newx][newy]) &&
2354           (LX + 2 * XS) / TILEX == newx &&
2355           (LY + 2 * YS) / TILEY == newy)
2356       {
2357         laser.num_edges--;
2358         ScanLaser();
2359       }
2360     }
2361   }
2362   else                          /* still moving on */
2363     DrawField_MM(x, y);
2364
2365   laser.redraw = TRUE;
2366 }
2367
2368 void ClickElement(int mx, int my, int button)
2369 {
2370   static unsigned int click_delay = 0;
2371   static int click_delay_value = CLICK_DELAY_SHORT;
2372   static boolean new_button = TRUE;
2373   int element;
2374   int x = (mx - SX) / TILEX, y = (my - SY) / TILEY;
2375
2376   if (button == MB_RELEASED)
2377   {
2378     new_button = TRUE;
2379     click_delay_value = CLICK_DELAY_SHORT;
2380
2381     /* release eventually hold auto-rotating mirror */
2382     RotateMirror(x, y, MB_RELEASED);
2383
2384     return;
2385   }
2386
2387   if (!DelayReached(&click_delay, click_delay_value) && !new_button)
2388     return;
2389
2390   if (button == MB_MIDDLEBUTTON)        /* middle button has no function */
2391     return;
2392
2393   if (!IN_PIX_FIELD(mx - SX, my - SY))
2394     return;
2395
2396   if (Feld[x][y] == EL_EMPTY)
2397     return;
2398
2399   element = Feld[x][y];
2400
2401   if (IS_MIRROR(element) ||
2402       IS_BEAMER(element) ||
2403       IS_POLAR(element) ||
2404       IS_POLAR_CROSS(element) ||
2405       IS_DF_MIRROR(element) ||
2406       IS_DF_MIRROR_AUTO(element))
2407   {
2408     RotateMirror(x, y, button);
2409   }
2410   else if (IS_MCDUFFIN(element))
2411   {
2412     if (!laser.fuse_off)
2413     {
2414       DrawLaser(0, DL_LASER_DISABLED);
2415       /*
2416       BackToFront();
2417       */
2418     }
2419
2420     element = get_rotated_element(element, BUTTON_ROTATION(button));
2421     laser.start_angle = get_element_angle(element);
2422
2423     InitLaser();
2424
2425     Feld[x][y] = element;
2426     DrawField_MM(x, y);
2427     /*
2428     BackToFront();
2429     */
2430     if (!laser.fuse_off)
2431       ScanLaser();
2432   }
2433   else if (element == EL_FUSE_ON && laser.fuse_off)
2434   {
2435     if (x != laser.fuse_x || y != laser.fuse_y)
2436       return;
2437
2438     laser.fuse_off = FALSE;
2439     laser.fuse_x = laser.fuse_y = -1;
2440
2441     DrawGraphic_MM(x, y, IMG_MM_FUSE_ACTIVE);
2442     ScanLaser();
2443   }
2444   else if (element == EL_FUSE_ON && !laser.fuse_off && new_button)
2445   {
2446     laser.fuse_off = TRUE;
2447     laser.fuse_x = x;
2448     laser.fuse_y = y;
2449     laser.overloaded = FALSE;
2450
2451     DrawLaser(0, DL_LASER_DISABLED);
2452     DrawGraphic_MM(x, y, IMG_MM_FUSE);
2453   }
2454   else if (element == EL_LIGHTBALL)
2455   {
2456     Bang_MM(x, y);
2457     RaiseScore_MM(10);
2458     DrawLaser(0, DL_LASER_ENABLED);
2459   }
2460
2461   click_delay_value = (new_button ? CLICK_DELAY_LONG : CLICK_DELAY_SHORT);
2462   new_button = FALSE;
2463 }
2464
2465 void RotateMirror(int x, int y, int button)
2466 {
2467   static int hold_x = -1, hold_y = -1;
2468
2469   if (button == MB_RELEASED)
2470   {
2471     /* release eventually hold auto-rotating mirror */
2472     hold_x = -1;
2473     hold_y = -1;
2474
2475     return;
2476   }
2477
2478   if (IS_MIRROR(Feld[x][y]) ||
2479       IS_POLAR_CROSS(Feld[x][y]) ||
2480       IS_POLAR(Feld[x][y]) ||
2481       IS_BEAMER(Feld[x][y]) ||
2482       IS_DF_MIRROR(Feld[x][y]) ||
2483       IS_GRID_STEEL_AUTO(Feld[x][y]) ||
2484       IS_GRID_WOOD_AUTO(Feld[x][y]))
2485   {
2486     Feld[x][y] = get_rotated_element(Feld[x][y], BUTTON_ROTATION(button));
2487   }
2488   else if (IS_DF_MIRROR_AUTO(Feld[x][y]))
2489   {
2490     if (button == MB_LEFTBUTTON)
2491     {
2492       /* left mouse button only for manual adjustment, no auto-rotating;
2493          freeze mirror for until mouse button released */
2494       hold_x = x;
2495       hold_y = y;
2496     }
2497     else if (button == MB_RIGHTBUTTON && (hold_x != x || hold_y != y))
2498       Feld[x][y] = get_rotated_element(Feld[x][y], ROTATE_RIGHT);
2499   }
2500
2501   if (IS_GRID_STEEL_AUTO(Feld[x][y]) || IS_GRID_WOOD_AUTO(Feld[x][y]))
2502   {
2503     int edge = Hit[x][y];
2504
2505     DrawField_MM(x, y);
2506
2507     if (edge > 0)
2508     {
2509       DrawLaser(edge - 1, DL_LASER_DISABLED);
2510       ScanLaser();
2511     }
2512   }
2513   else if (ObjHit(x, y, HIT_POS_CENTER))
2514   {
2515     int edge = Hit[x][y];
2516
2517     if (edge == 0)
2518     {
2519       Error(ERR_WARN, "RotateMirror: inconsistent field Hit[][]!\n");
2520       edge = 1;
2521     }
2522
2523     DrawLaser(edge - 1, DL_LASER_DISABLED);
2524     ScanLaser();
2525   }
2526   else
2527   {
2528     int check = 1;
2529
2530     if (ObjHit(x, y, HIT_POS_EDGE | HIT_POS_BETWEEN))
2531       check = 2;
2532
2533     DrawField_MM(x, y);
2534
2535     if ((IS_BEAMER(Feld[x][y]) ||
2536          IS_POLAR(Feld[x][y]) ||
2537          IS_POLAR_CROSS(Feld[x][y])) && x == ELX && y == ELY)
2538     {
2539       check = 0;
2540
2541       if (IS_BEAMER(Feld[x][y]))
2542       {
2543 #if 0
2544         printf("TEST (%d, %d) [%d] [%d]\n",
2545                LX, LY,
2546                laser.beamer_edge, laser.beamer[1].num);
2547 #endif
2548
2549         laser.num_edges--;
2550       }
2551
2552       ScanLaser();
2553     }
2554
2555     if (check == 2)
2556       DrawLaser(0, DL_LASER_ENABLED);
2557   }
2558 }
2559
2560 void AutoRotateMirrors()
2561 {
2562   static unsigned int rotate_delay = 0;
2563   int x, y;
2564
2565   if (!DelayReached(&rotate_delay, AUTO_ROTATE_DELAY))
2566     return;
2567
2568   for (x=0; x<lev_fieldx; x++)
2569   {
2570     for (y=0; y<lev_fieldy; y++)
2571     {
2572       int element = Feld[x][y];
2573
2574       if (IS_DF_MIRROR_AUTO(element) ||
2575           IS_GRID_WOOD_AUTO(element) ||
2576           IS_GRID_STEEL_AUTO(element) ||
2577           element == EL_REFRACTOR)
2578         RotateMirror(x, y, MB_RIGHTBUTTON);
2579     }
2580   }
2581 }
2582
2583 boolean ObjHit(int obx, int oby, int bits)
2584 {
2585   int i;
2586
2587   obx *= TILEX;
2588   oby *= TILEY;
2589
2590   if (bits & HIT_POS_CENTER)
2591   {
2592     if (ReadPixel(drawto, SX + obx + 15, SY + oby + 15) == pen_ray)
2593       return TRUE;
2594   }
2595
2596   if (bits & HIT_POS_EDGE)
2597   {
2598     for(i=0; i<4; i++)
2599       if (ReadPixel(drawto,
2600                     SX + obx + 31 * (i % 2),
2601                     SY + oby + 31 * (i / 2)) == pen_ray)
2602         return TRUE;
2603   }
2604
2605   if (bits & HIT_POS_BETWEEN)
2606   {
2607     for(i=0; i<4; i++)
2608       if (ReadPixel(drawto,
2609                     SX + 4 + obx + 22 * (i % 2),
2610                     SY + 4 + oby + 22 * (i / 2)) == pen_ray)
2611         return TRUE;
2612   }
2613
2614   return FALSE;
2615 }
2616
2617 void DeletePacMan(int px, int py)
2618 {
2619   int i, j;
2620
2621   Bang_MM(px, py);
2622
2623   if (game_mm.num_pacman <= 1)
2624   {
2625     game_mm.num_pacman = 0;
2626     return;
2627   }
2628
2629   for(i=0; i<game_mm.num_pacman; i++)
2630     if (game_mm.pacman[i].x == px && game_mm.pacman[i].y == py)
2631       break;
2632
2633   game_mm.num_pacman--;
2634
2635   for(j=i; j<game_mm.num_pacman; j++)
2636   {
2637     game_mm.pacman[j].x = game_mm.pacman[j + 1].x;
2638     game_mm.pacman[j].y = game_mm.pacman[j + 1].y;
2639     game_mm.pacman[j].dir = game_mm.pacman[j + 1].dir;
2640   }
2641 }
2642
2643 void ColorCycling(void)
2644 {
2645   static int CC, Cc = 0;
2646
2647   static int color, old = 0xF00, new = 0x010, mult = 1;
2648   static unsigned short red, green, blue;
2649
2650   if (color_status == STATIC_COLORS)
2651     return;
2652
2653   CC = Counter();
2654
2655   if (CC < Cc || CC > Cc + 50)
2656   {
2657     Cc = CC;
2658
2659     color = old + new * mult;
2660     if (mult > 0)
2661       mult++;
2662     else
2663       mult--;
2664
2665     if (ABS(mult) == 16)
2666     {
2667       mult =- mult / 16;
2668       old = color;
2669       new = new << 4;
2670       if (new > 0x100)
2671         new = 0x001;
2672     }
2673
2674     red   = 0x0e00 * ((color & 0xF00) >> 8);
2675     green = 0x0e00 * ((color & 0x0F0) >> 4);
2676     blue  = 0x0e00 * ((color & 0x00F));
2677     SetRGB(pen_magicolor[0], red, green, blue);
2678
2679     red   = 0x1111 * ((color & 0xF00) >> 8);
2680     green = 0x1111 * ((color & 0x0F0) >> 4);
2681     blue  = 0x1111 * ((color & 0x00F));
2682     SetRGB(pen_magicolor[1], red, green, blue);
2683   }
2684 }
2685
2686 static void GameActions_MM_Ext(byte action[MAX_PLAYERS], boolean warp_mode)
2687 {
2688   static unsigned int action_delay = 0;
2689   static unsigned int pacman_delay = 0;
2690   static unsigned int energy_delay = 0;
2691   static unsigned int overload_delay = 0;
2692   int element;
2693   int x, y, i;
2694
2695   int r, d;
2696
2697   WaitUntilDelayReached(&action_delay, GameFrameDelay);
2698
2699   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
2700     Stop[x][y] = FALSE;
2701
2702   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
2703   {
2704     element = Feld[x][y];
2705
2706     if (!IS_MOVING(x, y) && CAN_MOVE(element))
2707       StartMoving_MM(x, y);
2708     else if (IS_MOVING(x, y))
2709       ContinueMoving_MM(x, y);
2710     else if (IS_EXPLODING(element))
2711       Explode_MM(x, y, Frame[x][y], EX_NORMAL);
2712     else if (element == EL_EXIT_OPENING)
2713       OpenExit(x, y);
2714     else if (element == EL_GRAY_BALL_OPENING)
2715       OpenSurpriseBall(x, y);
2716     else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_ICE)
2717       MeltIce(x, y);
2718     else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_AMOEBA)
2719       GrowAmoeba(x, y);
2720   }
2721
2722   AutoRotateMirrors();
2723
2724 #if 1
2725   /* !!! CHANGE THIS: REDRAW ONLY WHEN NEEDED !!! */
2726
2727   /* redraw after Explode_MM() ... */
2728   if (laser.redraw)
2729     DrawLaser(0, DL_LASER_ENABLED);
2730   laser.redraw = FALSE;
2731 #endif
2732
2733   CT = Counter();
2734
2735   if (game_mm.num_pacman && DelayReached(&pacman_delay, 250))
2736   {
2737     MovePacMen();
2738
2739     if (laser.num_damages > MAX_LASER_LEN && !laser.fuse_off)
2740     {
2741       DrawLaser(0, DL_LASER_DISABLED);
2742       ScanLaser();
2743     }
2744   }
2745
2746   if (DelayReached(&energy_delay, 4000))
2747   {
2748     game_mm.energy_left--;
2749     if (game_mm.energy_left >= 0)
2750     {
2751 #if 0
2752       BlitBitmap(pix[PIX_DOOR], drawto,
2753                  DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
2754                  ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
2755                  DX_ENERGY, DY_ENERGY);
2756 #endif
2757       redraw_mask |= REDRAW_DOOR_1;
2758     }
2759     else if (setup.time_limit)
2760     {
2761       int i;
2762
2763       for(i=15; i>=0; i--)
2764       {
2765 #if 0
2766         SetRGB(pen_ray, 0x0000, 0x0000, i * color_scale);
2767 #endif
2768         pen_ray = GetPixelFromRGB(window,
2769                                   native_mm_level.laser_red   * 0x11 * i,
2770                                   native_mm_level.laser_green * 0x11 * i,
2771                                   native_mm_level.laser_blue  * 0x11 * i);
2772         DrawLaser(0, DL_LASER_ENABLED);
2773         BackToFront();
2774         Delay(50);
2775       }
2776
2777       StopSound(SND_WARNTON);
2778       FadeMusic();
2779
2780       DrawLaser(0, DL_LASER_DISABLED);
2781       game_mm.game_over = TRUE;
2782       game_mm.game_over_cause = GAME_OVER_NO_ENERGY;
2783
2784 #if 0
2785       if (Request("Out of magic energy ! Play it again ?",
2786                   REQ_ASK | REQ_STAY_CLOSED))
2787       {
2788         InitGame();
2789       }
2790       else
2791       {
2792         game_status = MAINMENU;
2793         DrawMainMenu();
2794       }
2795 #endif
2796
2797       return;
2798     }
2799   }
2800
2801   element = laser.dest_element;
2802
2803 #if 0
2804   if (element != Feld[ELX][ELY])
2805   {
2806     printf("element == %d, Feld[ELX][ELY] == %d\n",
2807            element, Feld[ELX][ELY]);
2808   }
2809 #endif
2810
2811   if (!laser.overloaded && laser.overload_value == 0 &&
2812       element != EL_BOMB &&
2813       element != EL_MINE &&
2814       element != EL_BALL_GRAY &&
2815       element != EL_BLOCK_STONE &&
2816       element != EL_BLOCK_WOOD &&
2817       element != EL_FUSE_ON &&
2818       element != EL_FUEL_FULL &&
2819       !IS_WALL_ICE(element) &&
2820       !IS_WALL_AMOEBA(element))
2821     return;
2822
2823   if (((laser.overloaded && laser.overload_value < MAX_LASER_OVERLOAD) ||
2824        (!laser.overloaded && laser.overload_value > 0)) &&
2825       DelayReached(&overload_delay, 60 + !laser.overloaded * 120))
2826   {
2827     if (laser.overloaded)
2828       laser.overload_value++;
2829     else
2830       laser.overload_value--;
2831
2832     if (game_mm.cheat_no_overload)
2833     {
2834       laser.overloaded = FALSE;
2835       laser.overload_value = 0;
2836     }
2837
2838     if (laser.overload_value < MAX_LASER_OVERLOAD - 8)
2839     {
2840       int color_up = 0xFF * laser.overload_value / MAX_LASER_OVERLOAD;
2841       int color_down = 0xFF - color_up;
2842
2843 #if 0
2844       SetRGB(pen_ray, (laser.overload_value / 6) * color_scale, 0x0000,
2845              (15 - (laser.overload_value / 6)) * color_scale);
2846 #endif
2847       pen_ray = GetPixelFromRGB(window,
2848                                 (native_mm_level.laser_red  ? 0xFF : color_up),
2849                                 (native_mm_level.laser_green ? color_down : 0x00),
2850                                 (native_mm_level.laser_blue  ? color_down : 0x00));
2851       DrawLaser(0, DL_LASER_ENABLED);
2852       BackToFront();
2853     }
2854
2855     if (laser.overloaded)
2856     {
2857       if (setup.sound_loops)
2858         PlaySoundExt(SND_WARNTON, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT, SND_CTRL_PLAY_LOOP);
2859       else
2860         PlaySoundStereo(SND_WARNTON, SOUND_MAX_RIGHT);
2861     }
2862
2863     if (!laser.overloaded)
2864       StopSound(SND_WARNTON);
2865
2866     if (laser.overloaded)
2867     {
2868 #if 0
2869       BlitBitmap(pix[PIX_DOOR], drawto,
2870                  DOOR_GFX_PAGEX4 + XX_OVERLOAD,
2871                  DOOR_GFX_PAGEY1 + YY_OVERLOAD + OVERLOAD_YSIZE
2872                  - laser.overload_value,
2873                  OVERLOAD_XSIZE, laser.overload_value,
2874                  DX_OVERLOAD, DY_OVERLOAD + OVERLOAD_YSIZE
2875                  - laser.overload_value);
2876 #endif
2877       redraw_mask |= REDRAW_DOOR_1;
2878     }
2879     else
2880     {
2881 #if 0
2882       BlitBitmap(pix[PIX_DOOR], drawto,
2883                  DOOR_GFX_PAGEX5 + XX_OVERLOAD, DOOR_GFX_PAGEY1 + YY_OVERLOAD,
2884                  OVERLOAD_XSIZE, OVERLOAD_YSIZE - laser.overload_value,
2885                  DX_OVERLOAD, DY_OVERLOAD);
2886 #endif
2887       redraw_mask |= REDRAW_DOOR_1;
2888     }
2889
2890     if (laser.overload_value == MAX_LASER_OVERLOAD)
2891     {
2892       int i;
2893
2894       for(i=15; i>=0; i--)
2895       {
2896 #if 0
2897         SetRGB(pen_ray, i * color_scale, 0x0000, 0x0000);
2898 #endif
2899
2900         pen_ray = GetPixelFromRGB(window, 0x11 * i, 0x00, 0x00);
2901         DrawLaser(0, DL_LASER_ENABLED);
2902         BackToFront();
2903         Delay(50);
2904       }
2905
2906       DrawLaser(0, DL_LASER_DISABLED);
2907       game_mm.game_over = TRUE;
2908       game_mm.game_over_cause = GAME_OVER_OVERLOADED;
2909
2910 #if 0
2911       if (Request("Magic spell hit Mc Duffin ! Play it again ?",
2912                   REQ_ASK | REQ_STAY_CLOSED))
2913       {
2914         InitGame();
2915       }
2916       else
2917       {
2918         game_status = MAINMENU;
2919         DrawMainMenu();
2920       }
2921 #endif
2922
2923       return;
2924     }
2925   }
2926
2927   if (laser.fuse_off)
2928     return;
2929
2930   CT -= Ct;
2931
2932   if (element == EL_BOMB && CT > 1500)
2933   {
2934     if (game_mm.cheat_no_explosion)
2935       return;
2936
2937 #if 0
2938     laser.num_damages--;
2939     DrawLaser(0, DL_LASER_DISABLED);
2940     laser.num_edges = 0;
2941 #endif
2942
2943     Bang_MM(ELX, ELY);
2944
2945     laser.dest_element = EL_EXPLODING_OPAQUE;
2946
2947 #if 0
2948     Bang_MM(ELX, ELY);
2949     laser.num_damages--;
2950     DrawLaser(0, DL_LASER_DISABLED);
2951
2952     laser.num_edges = 0;
2953     Bang_MM(laser.start_edge.x, laser.start_edge.y);
2954
2955     if (Request("Bomb killed Mc Duffin ! Play it again ?",
2956                 REQ_ASK | REQ_STAY_CLOSED))
2957     {
2958       InitGame();
2959     }
2960     else
2961     {
2962       game_status = MAINMENU;
2963       DrawMainMenu();
2964     }
2965 #endif
2966
2967     return;
2968   }
2969
2970   if (element == EL_FUSE_ON && CT > 500)
2971   {
2972     laser.fuse_off = TRUE;
2973     laser.fuse_x = ELX;
2974     laser.fuse_y = ELY;
2975     DrawLaser(0, DL_LASER_DISABLED);
2976     DrawGraphic_MM(ELX, ELY, IMG_MM_FUSE);
2977   }
2978
2979   if (element == EL_BALL_GRAY && CT > 1500)
2980   {
2981     static int new_elements[] =
2982     {
2983       EL_MIRROR_START,
2984       EL_MIRROR_FIXED_START,
2985       EL_POLAR_START,
2986       EL_POLAR_CROSS_START,
2987       EL_PACMAN_START,
2988       EL_KETTLE,
2989       EL_BOMB,
2990       EL_PRISM
2991     };
2992     int num_new_elements = sizeof(new_elements) / sizeof(int);
2993     int new_element = new_elements[RND(num_new_elements)];
2994
2995     Store[ELX][ELY] = new_element + RND(get_num_elements(new_element));
2996     Feld[ELX][ELY] = EL_GRAY_BALL_OPENING;
2997
2998     /* !!! CHECK AGAIN: Laser on Polarizer !!! */
2999     ScanLaser();
3000
3001     return;
3002
3003 #if 0
3004     int graphic;
3005
3006     switch (RND(5))
3007     {
3008       case 0:
3009         element = EL_MIRROR_START + RND(16);
3010         break;
3011       case 1:
3012         {
3013           int rnd = RND(3);
3014
3015           element = (rnd == 0 ? EL_KETTLE : rnd == 1 ? EL_BOMB : EL_PRISM);
3016         }
3017         break;
3018       default:
3019         {
3020           int rnd = RND(3);
3021
3022           element = (rnd == 0 ? EL_FUSE_ON :
3023                      rnd >= 1 && rnd <= 4 ? EL_PACMAN_RIGHT + rnd - 1 :
3024                      rnd >= 5 && rnd <= 20 ? EL_POLAR_START + rnd - 5 :
3025                      rnd >= 21 && rnd <= 24 ? EL_POLAR_CROSS_START + rnd - 21 :
3026                      EL_MIRROR_FIXED_START + rnd - 25);
3027         }
3028         break;
3029     }
3030
3031     graphic = el2gfx(element);
3032
3033     for(i=0; i<50; i++)
3034     {
3035       int x = RND(26);
3036       int y = RND(26);
3037
3038 #if 0
3039       BlitBitmap(pix[PIX_BACK], drawto,
3040                  SX + (graphic % GFX_PER_LINE) * TILEX + x,
3041                  SY + (graphic / GFX_PER_LINE) * TILEY + y, 6, 6,
3042                  SX + ELX * TILEX + x,
3043                  SY + ELY * TILEY + y);
3044 #endif
3045       MarkTileDirty(ELX, ELY);
3046       BackToFront();
3047
3048       DrawLaser(0, DL_LASER_ENABLED);
3049
3050       Delay(50);
3051     }
3052
3053     Feld[ELX][ELY] = element;
3054     DrawField_MM(ELX, ELY);
3055
3056 #if 0
3057     printf("NEW ELEMENT: (%d, %d)\n", ELX, ELY);
3058 #endif
3059
3060     /* above stuff: GRAY BALL -> PRISM !!! */
3061 /*
3062     LX = ELX * TILEX + 14;
3063     LY = ELY * TILEY + 14;
3064     if (laser.current_angle == (laser.current_angle >> 1) << 1)
3065       OK = 8;
3066     else
3067       OK = 4;
3068     LX -= OK * XS;
3069     LY -= OK * YS;
3070
3071     laser.num_edges -= 2;
3072     laser.num_damages--;
3073 */
3074
3075 #if 0
3076     for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
3077       if (laser.damage[i].is_mirror)
3078         break;
3079
3080     if (i > 0)
3081       DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3082     else
3083       DrawLaser(0, DL_LASER_DISABLED);
3084 #else
3085     DrawLaser(0, DL_LASER_DISABLED);
3086 #endif
3087
3088     ScanLaser();
3089
3090     /*
3091     printf("TEST ELEMENT: %d\n", Feld[0][0]);
3092     */
3093 #endif
3094
3095     return;
3096   }
3097
3098   if (IS_WALL_ICE(element) && CT > 1000)
3099   {
3100     PlaySoundStereo(SND_SLURP, ST(ELX));
3101
3102
3103
3104     {
3105       Feld[ELX][ELY] = Feld[ELX][ELY] - EL_WALL_ICE + EL_WALL_CHANGING;
3106       Store[ELX][ELY] = EL_WALL_ICE;
3107       Store2[ELX][ELY] = laser.wall_mask;
3108
3109       laser.dest_element = Feld[ELX][ELY];
3110
3111       return;
3112     }
3113
3114
3115
3116
3117     for(i=0; i<5; i++)
3118     {
3119       int phase = i + 1;
3120
3121       if (i == 4)
3122       {
3123         Feld[ELX][ELY] &= (laser.wall_mask ^ 0xFF);
3124         phase = 0;
3125       }
3126
3127       DrawWallsAnimation_MM(ELX, ELY, Feld[ELX][ELY], phase, laser.wall_mask);
3128       BackToFront();
3129       Delay(100);
3130     }
3131
3132     if (Feld[ELX][ELY] == EL_WALL_ICE)
3133       Feld[ELX][ELY] = EL_EMPTY;
3134
3135 /*
3136     laser.num_edges--;
3137     LX = laser.edge[laser.num_edges].x - (SX + 2);
3138     LY = laser.edge[laser.num_edges].y - (SY + 2);
3139 */
3140
3141     for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
3142       if (laser.damage[i].is_mirror)
3143         break;
3144
3145     if (i > 0)
3146       DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3147     else
3148       DrawLaser(0, DL_LASER_DISABLED);
3149
3150     ScanLaser();
3151
3152     return;
3153   }
3154
3155   if (IS_WALL_AMOEBA(element) && CT > 1200)
3156   {
3157     int k1, k2, k3, dx, dy, de, dm;
3158     int element2 = Feld[ELX][ELY];
3159
3160     if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element))
3161       return;
3162
3163     for (i = laser.num_damages - 1; i>=0; i--)
3164       if (laser.damage[i].is_mirror)
3165         break;
3166
3167     r = laser.num_edges;
3168     d = laser.num_damages;
3169     k1 = i;
3170
3171     if (k1 > 0)
3172     {
3173       int x, y;
3174
3175       DrawLaser(laser.damage[k1].edge - 1, DL_LASER_DISABLED);
3176
3177       laser.num_edges++;
3178       DrawLaser(0, DL_LASER_ENABLED);
3179       laser.num_edges--;
3180
3181       x = laser.damage[k1].x;
3182       y = laser.damage[k1].y;
3183       DrawField_MM(x, y);
3184     }
3185
3186     for(i=0; i<4; i++)
3187     {
3188       if (laser.wall_mask & (1 << i))
3189       {
3190         if (ReadPixel(drawto,
3191                       SX + ELX * TILEX + 14 + (i % 2) * 2,
3192                       SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3193           break;
3194         if (ReadPixel(drawto,
3195                       SX + ELX * TILEX + 31 * (i % 2),
3196                       SY + ELY * TILEY + 14 + (i / 2) * 2) == pen_ray)
3197           break;
3198       }
3199     }
3200
3201     k2 = i;
3202
3203     for(i=0; i<4; i++)
3204     {
3205       if (laser.wall_mask & (1 << i))
3206       {
3207         if (ReadPixel(drawto,
3208                       SX + ELX * TILEX + 31 * (i % 2),
3209                       SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3210           break;
3211       }
3212     }
3213
3214     k3 = i;
3215
3216     if (laser.num_beamers > 0 ||
3217         k1 < 1 || k2 < 4 || k3 < 4 ||
3218         ReadPixel(drawto, SX + ELX * TILEX + 14, SY + ELY * TILEY + 14)
3219         == pen_ray)
3220     {
3221       laser.num_edges = r;
3222       laser.num_damages = d;
3223       DrawLaser(0, DL_LASER_DISABLED);
3224     }
3225
3226     Feld[ELX][ELY] = element | laser.wall_mask;
3227     dx = ELX;
3228     dy = ELY;
3229     de = Feld[ELX][ELY];
3230     dm = laser.wall_mask;
3231
3232
3233
3234 #if 1
3235     {
3236       int x = ELX, y = ELY;
3237       int wall_mask = laser.wall_mask;
3238
3239
3240       ScanLaser();
3241       DrawLaser(0, DL_LASER_ENABLED);
3242
3243       PlaySoundStereo(SND_AMOEBE, ST(dx));
3244
3245
3246
3247       Feld[x][y] = Feld[x][y] - EL_WALL_AMOEBA + EL_WALL_CHANGING;
3248       Store[x][y] = EL_WALL_AMOEBA;
3249       Store2[x][y] = wall_mask;
3250
3251       return;
3252     }
3253 #endif
3254
3255
3256
3257     DrawWallsAnimation_MM(dx, dy, de, 4, dm);
3258     ScanLaser();
3259     DrawLaser(0, DL_LASER_ENABLED);
3260
3261     PlaySoundStereo(SND_AMOEBE, ST(dx));
3262
3263     for(i=4; i>=0; i--)
3264     {
3265       DrawWallsAnimation_MM(dx, dy, de, i, dm);
3266       BackToFront();
3267       Delay(20);
3268     }
3269
3270     DrawLaser(0, DL_LASER_ENABLED);
3271
3272     return;
3273   }
3274
3275   if ((element == EL_BLOCK_WOOD || element == EL_BLOCK_STONE) &&
3276       laser.stops_inside_element && CT > 1500)
3277   {
3278     int x, y;
3279     int k;
3280
3281     if (ABS(XS) > ABS(YS))
3282       k = 0;
3283     else
3284       k = 1;
3285     if (XS < YS)
3286       k += 2;
3287
3288     for(i=0; i<4; i++)
3289     {
3290       if (i)
3291         k++;
3292       if (k > 3)
3293         k=0;
3294
3295       x = ELX + Step[k * 4].x;
3296       y = ELY + Step[k * 4].y;
3297
3298       if (!IN_LEV_FIELD(x, y) || Feld[x][y] != EL_EMPTY)
3299         continue;
3300
3301       if (ObjHit(x, y, HIT_POS_CENTER | HIT_POS_EDGE | HIT_POS_BETWEEN))
3302         continue;
3303
3304       break;
3305     }
3306
3307     if (i > 3)
3308     {
3309       laser.overloaded = (element == EL_BLOCK_STONE);
3310       return;
3311     }
3312
3313     PlaySoundStereo(SND_BONG, ST(ELX));
3314
3315     Feld[ELX][ELY] = 0;
3316     Feld[x][y] = element;
3317
3318     DrawGraphic_MM(ELX, ELY, IMG_EMPTY);
3319     DrawField_MM(x, y);
3320
3321     if (element == EL_BLOCK_STONE && Box[ELX][ELY])
3322     {
3323       DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
3324       DrawLaser(laser.num_edges - 1, DL_LASER_ENABLED);
3325     }
3326
3327     ScanLaser();
3328
3329     return;
3330   }
3331
3332   if (element == EL_FUEL_FULL && CT > 200)
3333   {
3334     for(i=game_mm.energy_left; i<=MAX_LASER_ENERGY; i+=2)
3335     {
3336 #if 0
3337       BlitBitmap(pix[PIX_DOOR], drawto,
3338                  DOOR_GFX_PAGEX4 + XX_ENERGY,
3339                  DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
3340                  ENERGY_XSIZE, i, DX_ENERGY,
3341                  DY_ENERGY + ENERGY_YSIZE - i);
3342 #endif
3343
3344       redraw_mask |= REDRAW_DOOR_1;
3345       BackToFront();
3346
3347       Delay(20);
3348     }
3349
3350     game_mm.energy_left = MAX_LASER_ENERGY;
3351     Feld[ELX][ELY] = EL_FUEL_EMPTY;
3352     DrawField_MM(ELX, ELY);
3353
3354     DrawLaser(0, DL_LASER_ENABLED);
3355
3356     return;
3357   }
3358
3359   return;
3360 }
3361
3362 void GameActions_MM(byte action[MAX_PLAYERS], boolean warp_mode)
3363 {
3364   if (!button_status)
3365     ClickElement(0, 0, MB_NOT_PRESSED);
3366
3367   GameActions_MM_Ext(action, warp_mode);
3368 }
3369
3370 void MovePacMen()
3371 {
3372   static int p = -1;
3373   int mx, my, ox, oy, nx, ny;
3374   int element;
3375   int l;
3376
3377   if (++p >= game_mm.num_pacman)
3378     p = 0;
3379   game_mm.pacman[p].dir--;
3380
3381   for(l=1; l<5; l++)
3382   {
3383     game_mm.pacman[p].dir++;
3384
3385     if (game_mm.pacman[p].dir > 4)
3386       game_mm.pacman[p].dir = 1;
3387
3388     if (game_mm.pacman[p].dir % 2)
3389     {
3390       mx = 0;
3391       my = game_mm.pacman[p].dir - 2;
3392     }
3393     else
3394     {
3395       my = 0;
3396       mx = 3 - game_mm.pacman[p].dir;
3397     }
3398
3399     ox = game_mm.pacman[p].x;
3400     oy = game_mm.pacman[p].y;
3401     nx = ox + mx;
3402     ny = oy + my;
3403     element = Feld[nx][ny];
3404     if (nx < 0 || nx > 15 || ny < 0 || ny > 11)
3405       continue;
3406
3407     if (!IS_EATABLE4PACMAN(element))
3408       continue;
3409
3410     if (ObjHit(nx, ny, HIT_POS_CENTER))
3411       continue;
3412
3413     Feld[ox][oy] = EL_EMPTY;
3414     Feld[nx][ny] =
3415       EL_PACMAN_RIGHT - 1 +
3416       (game_mm.pacman[p].dir - 1 +
3417        (game_mm.pacman[p].dir % 2) * 2);
3418
3419     game_mm.pacman[p].x = nx;
3420     game_mm.pacman[p].y = ny;
3421
3422     DrawGraphic_MM(ox, oy, IMG_EMPTY);
3423
3424     if (element != EL_EMPTY)
3425     {
3426       int graphic = el2gfx(Feld[nx][ny]);
3427       Bitmap *bitmap;
3428       int src_x, src_y;
3429       int i;
3430
3431       getGraphicSource(graphic, 0, &bitmap, &src_x, &src_y);
3432
3433       CT = Counter();
3434       ox = SX + ox * TILEX;
3435       oy = SY + oy * TILEY;
3436
3437       for(i=1; i<33; i+=2)
3438         BlitBitmap(bitmap, window,
3439                    src_x, src_y, TILEX, TILEY,
3440                    ox + i * mx, oy + i * my);
3441       Ct = Ct + Counter() - CT;
3442     }
3443     DrawField_MM(nx, ny);
3444     BackToFront();
3445
3446     if (!laser.fuse_off)
3447     {
3448       DrawLaser(0, DL_LASER_ENABLED);
3449
3450       if (ObjHit(nx, ny, HIT_POS_BETWEEN))
3451       {
3452         AddDamagedField(nx, ny);
3453         laser.damage[laser.num_damages - 1].edge = 0;
3454       }
3455     }
3456
3457     if (element == EL_BOMB)
3458     {
3459       DeletePacMan(nx, ny);
3460     }
3461
3462     if (IS_WALL_AMOEBA(element) &&
3463         (LX + 2 * XS) / TILEX == nx &&
3464         (LY + 2 * YS) / TILEY == ny)
3465     {
3466       laser.num_edges--;
3467       ScanLaser();
3468     }
3469     break;
3470   }
3471 }
3472
3473 void GameWon_MM()
3474 {
3475   int hi_pos;
3476   boolean raise_level = FALSE;
3477
3478 #if 0
3479   if (local_player->MovPos)
3480     return;
3481
3482   local_player->LevelSolved = FALSE;
3483 #endif
3484
3485   if (game_mm.energy_left)
3486   {
3487     if (setup.sound_loops)
3488       PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT, SND_CTRL_PLAY_LOOP);
3489
3490     while(game_mm.energy_left > 0)
3491     {
3492       if (!setup.sound_loops)
3493         PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3494
3495       /*
3496       if (game_mm.energy_left > 0 && !(game_mm.energy_left % 10))
3497         RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3498       */
3499
3500       RaiseScore_MM(5);
3501
3502       game_mm.energy_left--;
3503       if (game_mm.energy_left >= 0)
3504       {
3505 #if 0
3506         BlitBitmap(pix[PIX_DOOR], drawto,
3507                    DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
3508                    ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
3509                    DX_ENERGY, DY_ENERGY);
3510 #endif
3511         redraw_mask |= REDRAW_DOOR_1;
3512       }
3513
3514       BackToFront();
3515       Delay(10);
3516     }
3517
3518     if (setup.sound_loops)
3519       StopSound(SND_SIRR);
3520   }
3521   else if (native_mm_level.time == 0)           /* level without time limit */
3522   {
3523     if (setup.sound_loops)
3524       PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT, SND_CTRL_PLAY_LOOP);
3525
3526     while(TimePlayed < 999)
3527     {
3528       if (!setup.sound_loops)
3529         PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3530       if (TimePlayed < 999 && !(TimePlayed % 10))
3531         RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3532       if (TimePlayed < 900 && !(TimePlayed % 10))
3533         TimePlayed += 10;
3534       else
3535         TimePlayed++;
3536
3537       /*
3538       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
3539       */
3540
3541       BackToFront();
3542       Delay(10);
3543     }
3544
3545     if (setup.sound_loops)
3546       StopSound(SND_SIRR);
3547   }
3548
3549 #if 0
3550   FadeSounds();
3551 #endif
3552
3553   CloseDoor(DOOR_CLOSE_1);
3554
3555   Request("Level solved !", REQ_CONFIRM);
3556
3557   if (level_nr == leveldir_current->handicap_level)
3558   {
3559     leveldir_current->handicap_level++;
3560     SaveLevelSetup_SeriesInfo();
3561   }
3562
3563   if (level_editor_test_game)
3564     game_mm.score = -1;         /* no highscore when playing from editor */
3565   else if (level_nr < leveldir_current->last_level)
3566     raise_level = TRUE;         /* advance to next level */
3567
3568   if ((hi_pos = NewHiScore_MM()) >= 0)
3569   {
3570     game_status = HALLOFFAME;
3571     // DrawHallOfFame(hi_pos);
3572     if (raise_level)
3573       level_nr++;
3574   }
3575   else
3576   {
3577     game_status = MAINMENU;
3578     if (raise_level)
3579       level_nr++;
3580     // DrawMainMenu();
3581   }
3582
3583   BackToFront();
3584 }
3585
3586 int NewHiScore_MM()
3587 {
3588   int k, l;
3589   int position = -1;
3590
3591   // LoadScore(level_nr);
3592
3593   if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
3594       game_mm.score < highscore[MAX_SCORE_ENTRIES - 1].Score)
3595     return -1;
3596
3597   for (k=0; k<MAX_SCORE_ENTRIES; k++)
3598   {
3599     if (game_mm.score > highscore[k].Score)
3600     {
3601       /* player has made it to the hall of fame */
3602
3603       if (k < MAX_SCORE_ENTRIES - 1)
3604       {
3605         int m = MAX_SCORE_ENTRIES - 1;
3606
3607 #ifdef ONE_PER_NAME
3608         for (l=k; l<MAX_SCORE_ENTRIES; l++)
3609           if (!strcmp(setup.player_name, highscore[l].Name))
3610             m = l;
3611         if (m == k)     /* player's new highscore overwrites his old one */
3612           goto put_into_list;
3613 #endif
3614
3615         for (l=m; l>k; l--)
3616         {
3617           strcpy(highscore[l].Name, highscore[l - 1].Name);
3618           highscore[l].Score = highscore[l - 1].Score;
3619         }
3620       }
3621
3622 #ifdef ONE_PER_NAME
3623       put_into_list:
3624 #endif
3625       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3626       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3627       highscore[k].Score = game_mm.score;
3628       position = k;
3629       break;
3630     }
3631
3632 #ifdef ONE_PER_NAME
3633     else if (!strncmp(setup.player_name, highscore[k].Name,
3634                       MAX_PLAYER_NAME_LEN))
3635       break;    /* player already there with a higher score */
3636 #endif
3637
3638   }
3639
3640   // if (position >= 0)
3641   //   SaveScore(level_nr);
3642
3643   return position;
3644 }
3645
3646 static void InitMovingField_MM(int x, int y, int direction)
3647 {
3648   int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3649   int newy = y + (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3650
3651   MovDir[x][y] = direction;
3652   MovDir[newx][newy] = direction;
3653   if (Feld[newx][newy] == EL_EMPTY)
3654     Feld[newx][newy] = EL_BLOCKED;
3655 }
3656
3657 static void Moving2Blocked_MM(int x, int y, int *goes_to_x, int *goes_to_y)
3658 {
3659   int direction = MovDir[x][y];
3660   int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3661   int newy = y + (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3662
3663   *goes_to_x = newx;
3664   *goes_to_y = newy;
3665 }
3666
3667 static void Blocked2Moving_MM(int x, int y,
3668                               int *comes_from_x, int *comes_from_y)
3669 {
3670   int oldx = x, oldy = y;
3671   int direction = MovDir[x][y];
3672
3673   if (direction == MV_LEFT)
3674     oldx++;
3675   else if (direction == MV_RIGHT)
3676     oldx--;
3677   else if (direction == MV_UP)
3678     oldy++;
3679   else if (direction == MV_DOWN)
3680     oldy--;
3681
3682   *comes_from_x = oldx;
3683   *comes_from_y = oldy;
3684 }
3685
3686 static int MovingOrBlocked2Element_MM(int x, int y)
3687 {
3688   int element = Feld[x][y];
3689
3690   if (element == EL_BLOCKED)
3691   {
3692     int oldx, oldy;
3693
3694     Blocked2Moving_MM(x, y, &oldx, &oldy);
3695     return Feld[oldx][oldy];
3696   }
3697   else
3698     return element;
3699 }
3700
3701 #if 0
3702 static void RemoveField(int x, int y)
3703 {
3704   Feld[x][y] = EL_EMPTY;
3705   MovPos[x][y] = 0;
3706   MovDir[x][y] = 0;
3707   MovDelay[x][y] = 0;
3708 }
3709 #endif
3710
3711 static void RemoveMovingField_MM(int x, int y)
3712 {
3713   int oldx = x, oldy = y, newx = x, newy = y;
3714
3715   if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
3716     return;
3717
3718   if (IS_MOVING(x, y))
3719   {
3720     Moving2Blocked_MM(x, y, &newx, &newy);
3721     if (Feld[newx][newy] != EL_BLOCKED)
3722       return;
3723   }
3724   else if (Feld[x][y] == EL_BLOCKED)
3725   {
3726     Blocked2Moving_MM(x, y, &oldx, &oldy);
3727     if (!IS_MOVING(oldx, oldy))
3728       return;
3729   }
3730
3731   Feld[oldx][oldy] = EL_EMPTY;
3732   Feld[newx][newy] = EL_EMPTY;
3733   MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
3734   MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
3735
3736   DrawLevelField_MM(oldx, oldy);
3737   DrawLevelField_MM(newx, newy);
3738 }
3739
3740 void PlaySoundLevel(int x, int y, int sound_nr)
3741 {
3742   int sx = SCREENX(x), sy = SCREENY(y);
3743   int volume, stereo;
3744   int silence_distance = 8;
3745
3746   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
3747       (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
3748     return;
3749
3750   if (!IN_LEV_FIELD(x, y) ||
3751       sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
3752       sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
3753     return;
3754
3755   volume = SOUND_MAX_VOLUME;
3756
3757 #ifndef MSDOS
3758   stereo = (sx - SCR_FIELDX/2) * 12;
3759 #else
3760   stereo = SOUND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
3761   if (stereo > SOUND_MAX_RIGHT)
3762     stereo = SOUND_MAX_RIGHT;
3763   if (stereo < SOUND_MAX_LEFT)
3764     stereo = SOUND_MAX_LEFT;
3765 #endif
3766
3767   if (!IN_SCR_FIELD(sx, sy))
3768   {
3769     int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
3770     int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
3771
3772     volume -= volume * (dx > dy ? dx : dy) / silence_distance;
3773   }
3774
3775   PlaySoundExt(sound_nr, volume, stereo, SND_CTRL_PLAY_SOUND);
3776 }
3777
3778 static void RaiseScore_MM(int value)
3779 {
3780   game_mm.score += value;
3781 #if 0
3782   DrawText(DX_SCORE, DY_SCORE, int2str(game_mm.score, 4),
3783            FONT_TEXT_2);
3784 #endif
3785 }
3786
3787 void RaiseScoreElement_MM(int element)
3788 {
3789   switch(element)
3790   {
3791     case EL_PACMAN:
3792       RaiseScore_MM(native_mm_level.score[SC_PACMAN]);
3793       break;
3794     case EL_KEY:
3795       RaiseScore_MM(native_mm_level.score[SC_KEY]);
3796       break;
3797     default:
3798       break;
3799   }
3800 }