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