fixed various graphics functions for Mirror Magic game engine
[rocksndiamonds.git] / src / game_mm / mm_game.c
1 // ============================================================================
2 // Mirror Magic -- McDuffin's Revenge
3 // ----------------------------------------------------------------------------
4 // (c) 1994-2017 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // mm_game.c
10 // ============================================================================
11
12 #include "main_mm.h"
13
14 #include "mm_main.h"
15 #include "mm_game.h"
16 #include "mm_tools.h"
17
18 /* graphic position values for game controls */
19 #define ENERGY_XSIZE            32
20 #define ENERGY_YSIZE            MAX_LASER_ENERGY
21 #define OVERLOAD_XSIZE          ENERGY_XSIZE
22 #define OVERLOAD_YSIZE          MAX_LASER_OVERLOAD
23
24 /* values for Explode_MM() */
25 #define EX_PHASE_START          0
26 #define EX_NORMAL               0
27 #define EX_KETTLE               1
28 #define EX_SHORT                2
29
30 /* special positions in the game control window (relative to control window) */
31 #define XX_LEVEL                36
32 #define YY_LEVEL                23
33 #define XX_KETTLES              29
34 #define YY_KETTLES              63
35 #define XX_SCORE                22
36 #define YY_SCORE                101
37 #define XX_ENERGY               8
38 #define YY_ENERGY               158
39 #define XX_OVERLOAD             60
40 #define YY_OVERLOAD             YY_ENERGY
41
42 /* special positions in the game control window (relative to main window) */
43 #define DX_LEVEL                (DX + XX_LEVEL)
44 #define DY_LEVEL                (DY + YY_LEVEL)
45 #define DX_KETTLES              (DX + XX_KETTLES)
46 #define DY_KETTLES              (DY + YY_KETTLES)
47 #define DX_SCORE                (DX + XX_SCORE)
48 #define DY_SCORE                (DY + YY_SCORE)
49 #define DX_ENERGY               (DX + XX_ENERGY)
50 #define DY_ENERGY               (DY + YY_ENERGY)
51 #define DX_OVERLOAD             (DX + XX_OVERLOAD)
52 #define DY_OVERLOAD             (DY + YY_OVERLOAD)
53
54 #define IS_LOOP_SOUND(s)        ((s) == SND_FUEL)
55 #define IS_MUSIC_SOUND(s)       ((s) == SND_TYGER || (s) == SND_VOYAGER)
56
57 /* game button identifiers */
58 #define GAME_CTRL_ID_LEFT               0
59 #define GAME_CTRL_ID_MIDDLE             1
60 #define GAME_CTRL_ID_RIGHT              2
61
62 #define NUM_GAME_BUTTONS                3
63
64 /* values for DrawLaser() */
65 #define DL_LASER_DISABLED               0
66 #define DL_LASER_ENABLED                1
67
68 /* values for 'click_delay_value' in ClickElement() */
69 #define CLICK_DELAY_SHORT       125
70 #define CLICK_DELAY_LONG        250
71 #define AUTO_ROTATE_DELAY       CLICK_DELAY_SHORT
72
73 /* forward declaration for internal use */
74 static int MovingOrBlocked2Element_MM(int, int);
75 static void Bang_MM(int, int);
76 static void RaiseScore_MM(int);
77 static void RemoveMovingField_MM(int, int);
78 static void InitMovingField_MM(int, int, int);
79 static void ContinueMoving_MM(int, int);
80 static void Moving2Blocked_MM(int, int, int *, int *);
81
82
83 static int get_element_angle(int element)
84 {
85   int element_phase = get_element_phase(element);
86
87   if (IS_MIRROR_FIXED(element) ||
88       IS_MCDUFFIN(element) ||
89       IS_LASER(element) ||
90       IS_RECEIVER(element))
91     return 4 * element_phase;
92   else
93     return element_phase;
94 }
95
96 static int get_opposite_angle(int angle)
97 {
98   int opposite_angle = angle + ANG_RAY_180;
99
100   /* make sure "opposite_angle" is in valid interval [0, 15] */
101   return (opposite_angle + 16) % 16;
102 }
103
104 static int get_mirrored_angle(int laser_angle, int mirror_angle)
105 {
106   int reflected_angle = 16 - laser_angle + mirror_angle;
107
108   /* make sure "reflected_angle" is in valid interval [0, 15] */
109   return (reflected_angle + 16) % 16;
110 }
111
112 static void InitMovDir_MM(int x, int y)
113 {
114   int element = Feld[x][y];
115   static int direction[3][4] =
116   {
117     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
118     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
119     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
120   };
121
122   switch(element)
123   {
124     case EL_PACMAN_RIGHT:
125     case EL_PACMAN_UP:
126     case EL_PACMAN_LEFT:
127     case EL_PACMAN_DOWN:
128       Feld[x][y] = EL_PACMAN;
129       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
130       break;
131
132     default:
133       break;
134   }
135 }
136
137 static void InitField(int x, int y, boolean init_game)
138 {
139   int element = Feld[x][y];
140
141   switch (element)
142   {
143     case EL_DF_EMPTY:
144       Feld[x][y] = EL_EMPTY;
145       break;
146
147     case EL_KETTLE:
148     case EL_CELL:
149       if (native_mm_level.auto_count_kettles)
150         game_mm.kettles_still_needed++;
151       break;
152
153     case EL_LIGHTBULB_OFF:
154       game_mm.lights_still_needed++;
155       break;
156
157     default:
158       if (IS_MIRROR(element) ||
159           IS_BEAMER_OLD(element) ||
160           IS_BEAMER(element) ||
161           IS_POLAR(element) ||
162           IS_POLAR_CROSS(element) ||
163           IS_DF_MIRROR(element) ||
164           IS_DF_MIRROR_AUTO(element) ||
165           IS_GRID_STEEL_AUTO(element) ||
166           IS_GRID_WOOD_AUTO(element) ||
167           IS_FIBRE_OPTIC(element))
168       {
169         if (IS_BEAMER_OLD(element))
170         {
171           Feld[x][y] = EL_BEAMER_BLUE_START + (element - EL_BEAMER_START);
172           element = Feld[x][y];
173         }
174
175         if (!IS_FIBRE_OPTIC(element))
176         {
177           static int steps_grid_auto = 0;
178
179           if (game_mm.num_cycle == 0)   /* initialize cycle steps for grids */
180             steps_grid_auto = RND(16) * (RND(2) ? -1 : +1);
181
182           if (IS_GRID_STEEL_AUTO(element) ||
183               IS_GRID_WOOD_AUTO(element))
184             game_mm.cycle[game_mm.num_cycle].steps = steps_grid_auto;
185           else
186             game_mm.cycle[game_mm.num_cycle].steps = RND(16) * (RND(2) ? -1 : +1);
187
188           game_mm.cycle[game_mm.num_cycle].x = x;
189           game_mm.cycle[game_mm.num_cycle].y = y;
190           game_mm.num_cycle++;
191         }
192
193         if (IS_BEAMER(element) || IS_FIBRE_OPTIC(element))
194         {
195           int beamer_nr = BEAMER_NR(element);
196           int nr = laser.beamer[beamer_nr][0].num;
197
198           laser.beamer[beamer_nr][nr].x = x;
199           laser.beamer[beamer_nr][nr].y = y;
200           laser.beamer[beamer_nr][nr].num = 1;
201         }
202       }
203       else if (IS_PACMAN(element))
204       {
205         InitMovDir_MM(x, y);
206       }
207       else if (IS_MCDUFFIN(element) || IS_LASER(element))
208       {
209         laser.start_edge.x = x;
210         laser.start_edge.y = y;
211         laser.start_angle = get_element_angle(element);
212       }
213
214       break;
215   }
216 }
217
218 static void InitCycleElements()
219 {
220   int i, j;
221
222   if (game_mm.num_cycle == 0)   /* no elements to cycle */
223     return;
224
225   for(i=0; i<16; i++)
226   {
227     for(j=0; j<game_mm.num_cycle; j++)
228     {
229       int x = game_mm.cycle[j].x;
230       int y = game_mm.cycle[j].y;
231       int step = SIGN(game_mm.cycle[j].steps);
232       int last_element = Feld[x][y];
233       int next_element = get_rotated_element(last_element, step);
234
235       if (!game_mm.cycle[j].steps)
236         continue;
237
238       Feld[x][y] = next_element;
239
240       DrawField_MM(x, y);
241       game_mm.cycle[j].steps -= step;
242     }
243
244     BackToFront();
245     ColorCycling();
246
247 #ifdef DEBUG
248     if (setup.quick_doors)
249       continue;
250 #endif
251
252     Delay(AUTO_ROTATE_DELAY);
253   }
254 }
255
256 static void InitLaser()
257 {
258   int start_element = Feld[laser.start_edge.x][laser.start_edge.y];
259   int step = (IS_LASER(start_element) ? 4 : 0);
260
261   LX = laser.start_edge.x * TILEX;
262   if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
263     LX += 14;
264   else
265     LX += (laser.start_angle == ANG_RAY_RIGHT ? 28 + step : 0 - step);
266
267   LY = laser.start_edge.y * TILEY;
268   if (laser.start_angle == ANG_RAY_UP || laser.start_angle == ANG_RAY_DOWN)
269     LY += (laser.start_angle == ANG_RAY_DOWN ? 28 + step : 0 - step);
270   else
271     LY += 14;
272
273   XS = 2 * Step[laser.start_angle].x;
274   YS = 2 * Step[laser.start_angle].y;
275
276   laser.current_angle = laser.start_angle;
277
278   laser.num_damages = 0;
279   laser.num_edges = 0;
280   laser.num_beamers = 0;
281   laser.beamer_edge[0] = 0;
282
283   AddLaserEdge(LX, LY);         /* set laser starting edge */
284
285   pen_ray = GetPixelFromRGB(window,
286                             native_mm_level.laser_red   * 0xFF,
287                             native_mm_level.laser_green * 0xFF,
288                             native_mm_level.laser_blue  * 0xFF);
289 }
290
291 void InitGameEngine_MM()
292 {
293   int i, x, y;
294
295   /* set global editor control values */
296   editor.draw_walls_masked = FALSE;
297
298   /* set global game control values */
299   game_mm.num_cycle = 0;
300   game_mm.num_pacman = 0;
301
302   game_mm.score = 0;
303   game_mm.energy_left = native_mm_level.time;
304   game_mm.kettles_still_needed =
305     (native_mm_level.auto_count_kettles ? 0 : native_mm_level.kettles_needed);
306   game_mm.lights_still_needed = 0;
307   game_mm.num_keys = 0;
308
309   game_mm.level_solved = FALSE;
310   game_mm.game_over = FALSE;
311   game_mm.game_over_cause = 0;
312
313   /* set global laser control values (must be set before "InitLaser()") */
314   laser.start_edge.x = 0;
315   laser.start_edge.y = 0;
316   laser.start_angle = 0;
317
318   for (i=0; i<MAX_NUM_BEAMERS; i++)
319     laser.beamer[i][0].num = laser.beamer[i][1].num = 0;
320
321   laser.overloaded = FALSE;
322   laser.overload_value = 0;
323   laser.fuse_off = FALSE;
324   laser.fuse_x = laser.fuse_y = -1;
325
326   laser.dest_element = EL_EMPTY;
327   laser.wall_mask = 0;
328
329   CT = Ct = 0;
330
331   for (x=0; x<lev_fieldx; x++)
332   {
333     for (y=0; y<lev_fieldy; y++)
334     {
335       Feld[x][y] = Ur[x][y];
336       Hit[x][y] = Box[x][y] = 0;
337       Angle[x][y] = 0;
338       MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
339       Store[x][y] = Store2[x][y] = 0;
340       Frame[x][y] = 0;
341       Stop[x][y] = FALSE;
342
343       InitField(x, y, TRUE);
344     }
345   }
346
347 #if 0
348   CloseDoor(DOOR_CLOSE_1);
349 #endif
350
351   DrawLevel_MM();
352   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     Store[x][y] = Store2[x][y] = 0;
2135     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2136     InitField(x, y, FALSE);
2137     DrawField_MM(x, y);
2138   }
2139   else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2140   {
2141     int graphic = IMG_MM_DEFAULT_EXPLODING;
2142     int graphic_phase = (phase / delay - 1);
2143     Bitmap *bitmap;
2144     int src_x, src_y;
2145
2146     if (Store2[x][y] == EX_KETTLE)
2147     {
2148       if (graphic_phase < 3)
2149         graphic = IMG_MM_KETTLE_EXPLODING;
2150       else if (graphic_phase < 5)
2151       {
2152         graphic_phase += 3;
2153       }
2154       else
2155       {
2156         graphic = IMG_EMPTY;
2157         graphic_phase = 0;
2158       }
2159     }
2160     else if (Store2[x][y] == EX_SHORT)
2161     {
2162       if (graphic_phase < 4)
2163         graphic_phase += 4;
2164       else
2165       {
2166         graphic = GFX_EMPTY;
2167         graphic_phase = 0;
2168       }
2169     }
2170
2171     getGraphicSource(graphic, graphic_phase, &bitmap, &src_x, &src_y);
2172
2173     BlitBitmap(bitmap, drawto_field, src_x, src_y, TILEX, TILEY,
2174                FX + x * TILEX, FY + y * TILEY);
2175     MarkTileDirty(x, y);
2176   }
2177 }
2178
2179 static void Bang_MM(int x, int y)
2180 {
2181   int element = Feld[x][y];
2182   int mode = EX_NORMAL;
2183
2184 #if 0
2185   DrawLaser(0, DL_LASER_ENABLED);
2186 #endif
2187
2188   switch(element)
2189   {
2190     case EL_KETTLE:
2191       mode = EX_KETTLE;
2192       break;
2193
2194     case EL_GATE_STONE:
2195     case EL_GATE_WOOD:
2196       mode = EX_SHORT;
2197       break;
2198
2199     default:
2200       mode = EX_NORMAL;
2201       break;
2202   }
2203
2204   if (IS_PACMAN(element))
2205     PlaySoundStereo(SND_QUIEK, ST(x));
2206   else if (element == EL_BOMB || IS_MCDUFFIN(element))
2207     PlaySoundStereo(SND_ROAAAR, ST(x));
2208   else if (element == EL_KEY)
2209     PlaySoundStereo(SND_KLING, ST(x));
2210   else
2211     PlaySoundStereo((mode == EX_SHORT ? SND_WHOOSH : SND_KABUMM), ST(x));
2212
2213   Explode_MM(x, y, EX_PHASE_START, mode);
2214 }
2215
2216 void TurnRound(int x, int y)
2217 {
2218   static struct
2219   {
2220     int x, y;
2221   } move_xy[] =
2222   {
2223     { 0, 0 },
2224     {-1, 0 },
2225     {+1, 0 },
2226     { 0, 0 },
2227     { 0, -1 },
2228     { 0, 0 }, { 0, 0 }, { 0, 0 },
2229     { 0, +1 }
2230   };
2231   static struct
2232   {
2233     int left, right, back;
2234   } turn[] =
2235   {
2236     { 0,        0,              0 },
2237     { MV_DOWN,  MV_UP,          MV_RIGHT },
2238     { MV_UP,    MV_DOWN,        MV_LEFT },
2239     { 0,        0,              0 },
2240     { MV_LEFT,  MV_RIGHT,       MV_DOWN },
2241     { 0,0,0 },  { 0,0,0 },      { 0,0,0 },
2242     { MV_RIGHT, MV_LEFT,        MV_UP }
2243   };
2244
2245   int element = Feld[x][y];
2246   int old_move_dir = MovDir[x][y];
2247   int right_dir = turn[old_move_dir].right;
2248   int back_dir = turn[old_move_dir].back;
2249   int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
2250   int right_x = x+right_dx, right_y = y+right_dy;
2251
2252   if (element == EL_PACMAN)
2253   {
2254     boolean can_turn_right = FALSE;
2255
2256     if (IN_LEV_FIELD(right_x, right_y) &&
2257         IS_EATABLE4PACMAN(Feld[right_x][right_y]))
2258       can_turn_right = TRUE;
2259
2260     if (can_turn_right)
2261       MovDir[x][y] = right_dir;
2262     else
2263       MovDir[x][y] = back_dir;
2264
2265     MovDelay[x][y] = 0;
2266   }
2267 }
2268
2269 static void StartMoving_MM(int x, int y)
2270 {
2271   int element = Feld[x][y];
2272
2273   if (Stop[x][y])
2274     return;
2275
2276   if (CAN_MOVE(element))
2277   {
2278     int newx, newy;
2279
2280     if (MovDelay[x][y])         /* wait some time before next movement */
2281     {
2282       MovDelay[x][y]--;
2283
2284       if (MovDelay[x][y])
2285         return;
2286     }
2287
2288     /* now make next step */
2289
2290     Moving2Blocked_MM(x, y, &newx, &newy);      /* get next screen position */
2291
2292     if (element == EL_PACMAN &&
2293         IN_LEV_FIELD(newx, newy) && IS_EATABLE4PACMAN(Feld[newx][newy]) &&
2294         !ObjHit(newx, newy, HIT_POS_CENTER))
2295     {
2296       Store[newx][newy] = Feld[newx][newy];
2297       Feld[newx][newy] = EL_EMPTY;
2298       DrawField_MM(newx, newy);
2299     }
2300     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy) ||
2301              ObjHit(newx, newy, HIT_POS_CENTER))
2302     {
2303       /* object was running against a wall */
2304
2305       TurnRound(x, y);
2306
2307       return;
2308     }
2309
2310     InitMovingField_MM(x, y, MovDir[x][y]);
2311   }
2312
2313   if (MovDir[x][y])
2314     ContinueMoving_MM(x, y);
2315 }
2316
2317 static void ContinueMoving_MM(int x, int y)
2318 {
2319   int element = Feld[x][y];
2320   int direction = MovDir[x][y];
2321   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2322   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
2323   int horiz_move = (dx!=0);
2324   int newx = x + dx, newy = y + dy;
2325   int step = (horiz_move ? dx : dy) * TILEX / 8;
2326
2327   MovPos[x][y] += step;
2328
2329   if (ABS(MovPos[x][y]) >= TILEX)       /* object reached its destination */
2330   {
2331     Feld[x][y] = EL_EMPTY;
2332     Feld[newx][newy] = element;
2333
2334     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2335     MovDelay[newx][newy] = 0;
2336
2337     if (!CAN_MOVE(element))
2338       MovDir[newx][newy] = 0;
2339
2340     DrawField_MM(x, y);
2341     DrawField_MM(newx, newy);
2342
2343     Stop[newx][newy] = TRUE;
2344
2345     if (element == EL_PACMAN)
2346     {
2347       if (Store[newx][newy] == EL_BOMB)
2348         Bang_MM(newx, newy);
2349
2350       if (IS_WALL_AMOEBA(Store[newx][newy]) &&
2351           (LX + 2 * XS) / TILEX == newx &&
2352           (LY + 2 * YS) / TILEY == newy)
2353       {
2354         laser.num_edges--;
2355         ScanLaser();
2356       }
2357     }
2358   }
2359   else                          /* still moving on */
2360     DrawField_MM(x, y);
2361
2362   laser.redraw = TRUE;
2363 }
2364
2365 void ClickElement(int mx, int my, int button)
2366 {
2367   static unsigned int click_delay = 0;
2368   static int click_delay_value = CLICK_DELAY_SHORT;
2369   static boolean new_button = TRUE;
2370   int element;
2371   int x = (mx - SX) / TILEX, y = (my - SY) / TILEY;
2372
2373   if (button == MB_RELEASED)
2374   {
2375     new_button = TRUE;
2376     click_delay_value = CLICK_DELAY_SHORT;
2377
2378     /* release eventually hold auto-rotating mirror */
2379     RotateMirror(x, y, MB_RELEASED);
2380
2381     return;
2382   }
2383
2384   if (!DelayReached(&click_delay, click_delay_value) && !new_button)
2385     return;
2386
2387   if (button == MB_MIDDLEBUTTON)        /* middle button has no function */
2388     return;
2389
2390   if (!IN_PIX_FIELD(mx - SX, my - SY))
2391     return;
2392
2393   if (Feld[x][y] == EL_EMPTY)
2394     return;
2395
2396   element = Feld[x][y];
2397
2398   if (IS_MIRROR(element) ||
2399       IS_BEAMER(element) ||
2400       IS_POLAR(element) ||
2401       IS_POLAR_CROSS(element) ||
2402       IS_DF_MIRROR(element) ||
2403       IS_DF_MIRROR_AUTO(element))
2404   {
2405     RotateMirror(x, y, button);
2406   }
2407   else if (IS_MCDUFFIN(element))
2408   {
2409     if (!laser.fuse_off)
2410     {
2411       DrawLaser(0, DL_LASER_DISABLED);
2412       /*
2413       BackToFront();
2414       */
2415     }
2416
2417     element = get_rotated_element(element, BUTTON_ROTATION(button));
2418     laser.start_angle = get_element_angle(element);
2419
2420     InitLaser();
2421
2422     Feld[x][y] = element;
2423     DrawField_MM(x, y);
2424     /*
2425     BackToFront();
2426     */
2427     if (!laser.fuse_off)
2428       ScanLaser();
2429   }
2430   else if (element == EL_FUSE_ON && laser.fuse_off)
2431   {
2432     if (x != laser.fuse_x || y != laser.fuse_y)
2433       return;
2434
2435     laser.fuse_off = FALSE;
2436     laser.fuse_x = laser.fuse_y = -1;
2437
2438     DrawGraphic_MM(x, y, IMG_MM_FUSE_ACTIVE);
2439     ScanLaser();
2440   }
2441   else if (element == EL_FUSE_ON && !laser.fuse_off && new_button)
2442   {
2443     laser.fuse_off = TRUE;
2444     laser.fuse_x = x;
2445     laser.fuse_y = y;
2446     laser.overloaded = FALSE;
2447
2448     DrawLaser(0, DL_LASER_DISABLED);
2449     DrawGraphic_MM(x, y, IMG_MM_FUSE);
2450   }
2451   else if (element == EL_LIGHTBALL)
2452   {
2453     Bang_MM(x, y);
2454     RaiseScore_MM(10);
2455     DrawLaser(0, DL_LASER_ENABLED);
2456   }
2457
2458   click_delay_value = (new_button ? CLICK_DELAY_LONG : CLICK_DELAY_SHORT);
2459   new_button = FALSE;
2460 }
2461
2462 void RotateMirror(int x, int y, int button)
2463 {
2464   static int hold_x = -1, hold_y = -1;
2465
2466   if (button == MB_RELEASED)
2467   {
2468     /* release eventually hold auto-rotating mirror */
2469     hold_x = -1;
2470     hold_y = -1;
2471
2472     return;
2473   }
2474
2475   if (IS_MIRROR(Feld[x][y]) ||
2476       IS_POLAR_CROSS(Feld[x][y]) ||
2477       IS_POLAR(Feld[x][y]) ||
2478       IS_BEAMER(Feld[x][y]) ||
2479       IS_DF_MIRROR(Feld[x][y]) ||
2480       IS_GRID_STEEL_AUTO(Feld[x][y]) ||
2481       IS_GRID_WOOD_AUTO(Feld[x][y]))
2482   {
2483     Feld[x][y] = get_rotated_element(Feld[x][y], BUTTON_ROTATION(button));
2484   }
2485   else if (IS_DF_MIRROR_AUTO(Feld[x][y]))
2486   {
2487     if (button == MB_LEFTBUTTON)
2488     {
2489       /* left mouse button only for manual adjustment, no auto-rotating;
2490          freeze mirror for until mouse button released */
2491       hold_x = x;
2492       hold_y = y;
2493     }
2494     else if (button == MB_RIGHTBUTTON && (hold_x != x || hold_y != y))
2495       Feld[x][y] = get_rotated_element(Feld[x][y], ROTATE_RIGHT);
2496   }
2497
2498   if (IS_GRID_STEEL_AUTO(Feld[x][y]) || IS_GRID_WOOD_AUTO(Feld[x][y]))
2499   {
2500     int edge = Hit[x][y];
2501
2502     DrawField_MM(x, y);
2503
2504     if (edge > 0)
2505     {
2506       DrawLaser(edge - 1, DL_LASER_DISABLED);
2507       ScanLaser();
2508     }
2509   }
2510   else if (ObjHit(x, y, HIT_POS_CENTER))
2511   {
2512     int edge = Hit[x][y];
2513
2514     if (edge == 0)
2515     {
2516       Error(ERR_WARN, "RotateMirror: inconsistent field Hit[][]!\n");
2517       edge = 1;
2518     }
2519
2520     DrawLaser(edge - 1, DL_LASER_DISABLED);
2521     ScanLaser();
2522   }
2523   else
2524   {
2525     int check = 1;
2526
2527     if (ObjHit(x, y, HIT_POS_EDGE | HIT_POS_BETWEEN))
2528       check = 2;
2529
2530     DrawField_MM(x, y);
2531
2532     if ((IS_BEAMER(Feld[x][y]) ||
2533          IS_POLAR(Feld[x][y]) ||
2534          IS_POLAR_CROSS(Feld[x][y])) && x == ELX && y == ELY)
2535     {
2536       check = 0;
2537
2538       if (IS_BEAMER(Feld[x][y]))
2539       {
2540 #if 0
2541         printf("TEST (%d, %d) [%d] [%d]\n",
2542                LX, LY,
2543                laser.beamer_edge, laser.beamer[1].num);
2544 #endif
2545
2546         laser.num_edges--;
2547       }
2548
2549       ScanLaser();
2550     }
2551
2552     if (check == 2)
2553       DrawLaser(0, DL_LASER_ENABLED);
2554   }
2555 }
2556
2557 void AutoRotateMirrors()
2558 {
2559   static unsigned int rotate_delay = 0;
2560   int x, y;
2561
2562   if (!DelayReached(&rotate_delay, AUTO_ROTATE_DELAY))
2563     return;
2564
2565   for (x=0; x<lev_fieldx; x++)
2566   {
2567     for (y=0; y<lev_fieldy; y++)
2568     {
2569       int element = Feld[x][y];
2570
2571       if (IS_DF_MIRROR_AUTO(element) ||
2572           IS_GRID_WOOD_AUTO(element) ||
2573           IS_GRID_STEEL_AUTO(element) ||
2574           element == EL_REFRACTOR)
2575         RotateMirror(x, y, MB_RIGHTBUTTON);
2576     }
2577   }
2578 }
2579
2580 boolean ObjHit(int obx, int oby, int bits)
2581 {
2582   int i;
2583
2584   obx *= TILEX;
2585   oby *= TILEY;
2586
2587   if (bits & HIT_POS_CENTER)
2588   {
2589     if (ReadPixel(drawto, SX + obx + 15, SY + oby + 15) == pen_ray)
2590       return TRUE;
2591   }
2592
2593   if (bits & HIT_POS_EDGE)
2594   {
2595     for(i=0; i<4; i++)
2596       if (ReadPixel(drawto,
2597                     SX + obx + 31 * (i % 2),
2598                     SY + oby + 31 * (i / 2)) == pen_ray)
2599         return TRUE;
2600   }
2601
2602   if (bits & HIT_POS_BETWEEN)
2603   {
2604     for(i=0; i<4; i++)
2605       if (ReadPixel(drawto,
2606                     SX + 4 + obx + 22 * (i % 2),
2607                     SY + 4 + oby + 22 * (i / 2)) == pen_ray)
2608         return TRUE;
2609   }
2610
2611   return FALSE;
2612 }
2613
2614 void DeletePacMan(int px, int py)
2615 {
2616   int i, j;
2617
2618   Bang_MM(px, py);
2619
2620   if (game_mm.num_pacman <= 1)
2621   {
2622     game_mm.num_pacman = 0;
2623     return;
2624   }
2625
2626   for(i=0; i<game_mm.num_pacman; i++)
2627     if (game_mm.pacman[i].x == px && game_mm.pacman[i].y == py)
2628       break;
2629
2630   game_mm.num_pacman--;
2631
2632   for(j=i; j<game_mm.num_pacman; j++)
2633   {
2634     game_mm.pacman[j].x = game_mm.pacman[j + 1].x;
2635     game_mm.pacman[j].y = game_mm.pacman[j + 1].y;
2636     game_mm.pacman[j].dir = game_mm.pacman[j + 1].dir;
2637   }
2638 }
2639
2640 void ColorCycling(void)
2641 {
2642   static int CC, Cc = 0;
2643
2644   static int color, old = 0xF00, new = 0x010, mult = 1;
2645   static unsigned short red, green, blue;
2646
2647   if (color_status == STATIC_COLORS)
2648     return;
2649
2650   CC = Counter();
2651
2652   if (CC < Cc || CC > Cc + 50)
2653   {
2654     Cc = CC;
2655
2656     color = old + new * mult;
2657     if (mult > 0)
2658       mult++;
2659     else
2660       mult--;
2661
2662     if (ABS(mult) == 16)
2663     {
2664       mult =- mult / 16;
2665       old = color;
2666       new = new << 4;
2667       if (new > 0x100)
2668         new = 0x001;
2669     }
2670
2671     red   = 0x0e00 * ((color & 0xF00) >> 8);
2672     green = 0x0e00 * ((color & 0x0F0) >> 4);
2673     blue  = 0x0e00 * ((color & 0x00F));
2674     SetRGB(pen_magicolor[0], red, green, blue);
2675
2676     red   = 0x1111 * ((color & 0xF00) >> 8);
2677     green = 0x1111 * ((color & 0x0F0) >> 4);
2678     blue  = 0x1111 * ((color & 0x00F));
2679     SetRGB(pen_magicolor[1], red, green, blue);
2680   }
2681 }
2682
2683 void GameActions_MM(byte action[MAX_PLAYERS], boolean warp_mode)
2684 {
2685   static unsigned int action_delay = 0;
2686   static unsigned int pacman_delay = 0;
2687   static unsigned int energy_delay = 0;
2688   static unsigned int overload_delay = 0;
2689   int element;
2690   int x, y, i;
2691
2692   int r, d;
2693
2694   WaitUntilDelayReached(&action_delay, GameFrameDelay);
2695
2696   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
2697     Stop[x][y] = FALSE;
2698
2699   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
2700   {
2701     element = Feld[x][y];
2702
2703     if (!IS_MOVING(x, y) && CAN_MOVE(element))
2704       StartMoving_MM(x, y);
2705     else if (IS_MOVING(x, y))
2706       ContinueMoving_MM(x, y);
2707     else if (IS_EXPLODING(element))
2708       Explode_MM(x, y, Frame[x][y], EX_NORMAL);
2709     else if (element == EL_EXIT_OPENING)
2710       OpenExit(x, y);
2711     else if (element == EL_GRAY_BALL_OPENING)
2712       OpenSurpriseBall(x, y);
2713     else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_ICE)
2714       MeltIce(x, y);
2715     else if (IS_WALL_CHANGING(element) && Store[x][y] == EL_WALL_AMOEBA)
2716       GrowAmoeba(x, y);
2717   }
2718
2719   AutoRotateMirrors();
2720
2721 #if 1
2722   /* !!! CHANGE THIS: REDRAW ONLY WHEN NEEDED !!! */
2723
2724   /* redraw after Explode_MM() ... */
2725   if (laser.redraw)
2726     DrawLaser(0, DL_LASER_ENABLED);
2727   laser.redraw = FALSE;
2728 #endif
2729
2730   CT = Counter();
2731
2732   if (game_mm.num_pacman && DelayReached(&pacman_delay, 250))
2733   {
2734     MovePacMen();
2735
2736     if (laser.num_damages > MAX_LASER_LEN && !laser.fuse_off)
2737     {
2738       DrawLaser(0, DL_LASER_DISABLED);
2739       ScanLaser();
2740     }
2741   }
2742
2743   if (DelayReached(&energy_delay, 4000))
2744   {
2745     game_mm.energy_left--;
2746     if (game_mm.energy_left >= 0)
2747     {
2748 #if 0
2749       BlitBitmap(pix[PIX_DOOR], drawto,
2750                  DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
2751                  ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
2752                  DX_ENERGY, DY_ENERGY);
2753 #endif
2754       redraw_mask |= REDRAW_DOOR_1;
2755     }
2756     else if (setup.time_limit)
2757     {
2758       int i;
2759
2760       for(i=15; i>=0; i--)
2761       {
2762 #if 0
2763         SetRGB(pen_ray, 0x0000, 0x0000, i * color_scale);
2764 #endif
2765         pen_ray = GetPixelFromRGB(window,
2766                                   native_mm_level.laser_red   * 0x11 * i,
2767                                   native_mm_level.laser_green * 0x11 * i,
2768                                   native_mm_level.laser_blue  * 0x11 * i);
2769         DrawLaser(0, DL_LASER_ENABLED);
2770         BackToFront();
2771         Delay(50);
2772       }
2773
2774       StopSound(SND_WARNTON);
2775       FadeMusic();
2776
2777       DrawLaser(0, DL_LASER_DISABLED);
2778       game_mm.game_over = TRUE;
2779       game_mm.game_over_cause = GAME_OVER_NO_ENERGY;
2780
2781 #if 0
2782       if (Request("Out of magic energy ! Play it again ?",
2783                   REQ_ASK | REQ_STAY_CLOSED))
2784       {
2785         InitGame();
2786       }
2787       else
2788       {
2789         game_status = MAINMENU;
2790         DrawMainMenu();
2791       }
2792 #endif
2793
2794       return;
2795     }
2796   }
2797
2798   element = laser.dest_element;
2799
2800 #if 0
2801   if (element != Feld[ELX][ELY])
2802   {
2803     printf("element == %d, Feld[ELX][ELY] == %d\n",
2804            element, Feld[ELX][ELY]);
2805   }
2806 #endif
2807
2808   if (!laser.overloaded && laser.overload_value == 0 &&
2809       element != EL_BOMB &&
2810       element != EL_MINE &&
2811       element != EL_BALL_GRAY &&
2812       element != EL_BLOCK_STONE &&
2813       element != EL_BLOCK_WOOD &&
2814       element != EL_FUSE_ON &&
2815       element != EL_FUEL_FULL &&
2816       !IS_WALL_ICE(element) &&
2817       !IS_WALL_AMOEBA(element))
2818     return;
2819
2820   if (((laser.overloaded && laser.overload_value < MAX_LASER_OVERLOAD) ||
2821        (!laser.overloaded && laser.overload_value > 0)) &&
2822       DelayReached(&overload_delay, 60 + !laser.overloaded * 120))
2823   {
2824     if (laser.overloaded)
2825       laser.overload_value++;
2826     else
2827       laser.overload_value--;
2828
2829     if (game_mm.cheat_no_overload)
2830     {
2831       laser.overloaded = FALSE;
2832       laser.overload_value = 0;
2833     }
2834
2835     if (laser.overload_value < MAX_LASER_OVERLOAD - 8)
2836     {
2837       int color_up = 0xFF * laser.overload_value / MAX_LASER_OVERLOAD;
2838       int color_down = 0xFF - color_up;
2839
2840 #if 0
2841       SetRGB(pen_ray, (laser.overload_value / 6) * color_scale, 0x0000,
2842              (15 - (laser.overload_value / 6)) * color_scale);
2843 #endif
2844       pen_ray = GetPixelFromRGB(window,
2845                                 (native_mm_level.laser_red  ? 0xFF : color_up),
2846                                 (native_mm_level.laser_green ? color_down : 0x00),
2847                                 (native_mm_level.laser_blue  ? color_down : 0x00));
2848       DrawLaser(0, DL_LASER_ENABLED);
2849       BackToFront();
2850     }
2851
2852     if (laser.overloaded)
2853     {
2854       if (setup.sound_loops)
2855         PlaySoundExt(SND_WARNTON, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT, SND_CTRL_PLAY_LOOP);
2856       else
2857         PlaySoundStereo(SND_WARNTON, SOUND_MAX_RIGHT);
2858     }
2859
2860     if (!laser.overloaded)
2861       StopSound(SND_WARNTON);
2862
2863     if (laser.overloaded)
2864     {
2865 #if 0
2866       BlitBitmap(pix[PIX_DOOR], drawto,
2867                  DOOR_GFX_PAGEX4 + XX_OVERLOAD,
2868                  DOOR_GFX_PAGEY1 + YY_OVERLOAD + OVERLOAD_YSIZE
2869                  - laser.overload_value,
2870                  OVERLOAD_XSIZE, laser.overload_value,
2871                  DX_OVERLOAD, DY_OVERLOAD + OVERLOAD_YSIZE
2872                  - laser.overload_value);
2873 #endif
2874       redraw_mask |= REDRAW_DOOR_1;
2875     }
2876     else
2877     {
2878 #if 0
2879       BlitBitmap(pix[PIX_DOOR], drawto,
2880                  DOOR_GFX_PAGEX5 + XX_OVERLOAD, DOOR_GFX_PAGEY1 + YY_OVERLOAD,
2881                  OVERLOAD_XSIZE, OVERLOAD_YSIZE - laser.overload_value,
2882                  DX_OVERLOAD, DY_OVERLOAD);
2883 #endif
2884       redraw_mask |= REDRAW_DOOR_1;
2885     }
2886
2887     if (laser.overload_value == MAX_LASER_OVERLOAD)
2888     {
2889       int i;
2890
2891       for(i=15; i>=0; i--)
2892       {
2893 #if 0
2894         SetRGB(pen_ray, i * color_scale, 0x0000, 0x0000);
2895 #endif
2896
2897         pen_ray = GetPixelFromRGB(window, 0x11 * i, 0x00, 0x00);
2898         DrawLaser(0, DL_LASER_ENABLED);
2899         BackToFront();
2900         Delay(50);
2901       }
2902
2903       DrawLaser(0, DL_LASER_DISABLED);
2904       game_mm.game_over = TRUE;
2905       game_mm.game_over_cause = GAME_OVER_OVERLOADED;
2906
2907 #if 0
2908       if (Request("Magic spell hit Mc Duffin ! Play it again ?",
2909                   REQ_ASK | REQ_STAY_CLOSED))
2910       {
2911         InitGame();
2912       }
2913       else
2914       {
2915         game_status = MAINMENU;
2916         DrawMainMenu();
2917       }
2918 #endif
2919
2920       return;
2921     }
2922   }
2923
2924   if (laser.fuse_off)
2925     return;
2926
2927   CT -= Ct;
2928
2929   if (element == EL_BOMB && CT > 1500)
2930   {
2931     if (game_mm.cheat_no_explosion)
2932       return;
2933
2934 #if 0
2935     laser.num_damages--;
2936     DrawLaser(0, DL_LASER_DISABLED);
2937     laser.num_edges = 0;
2938 #endif
2939
2940     Bang_MM(ELX, ELY);
2941
2942     laser.dest_element = EL_EXPLODING_OPAQUE;
2943
2944 #if 0
2945     Bang_MM(ELX, ELY);
2946     laser.num_damages--;
2947     DrawLaser(0, DL_LASER_DISABLED);
2948
2949     laser.num_edges = 0;
2950     Bang_MM(laser.start_edge.x, laser.start_edge.y);
2951
2952     if (Request("Bomb killed Mc Duffin ! Play it again ?",
2953                 REQ_ASK | REQ_STAY_CLOSED))
2954     {
2955       InitGame();
2956     }
2957     else
2958     {
2959       game_status = MAINMENU;
2960       DrawMainMenu();
2961     }
2962 #endif
2963
2964     return;
2965   }
2966
2967   if (element == EL_FUSE_ON && CT > 500)
2968   {
2969     laser.fuse_off = TRUE;
2970     laser.fuse_x = ELX;
2971     laser.fuse_y = ELY;
2972     DrawLaser(0, DL_LASER_DISABLED);
2973     DrawGraphic_MM(ELX, ELY, IMG_MM_FUSE);
2974   }
2975
2976   if (element == EL_BALL_GRAY && CT > 1500)
2977   {
2978     static int new_elements[] =
2979     {
2980       EL_MIRROR_START,
2981       EL_MIRROR_FIXED_START,
2982       EL_POLAR_START,
2983       EL_POLAR_CROSS_START,
2984       EL_PACMAN_START,
2985       EL_KETTLE,
2986       EL_BOMB,
2987       EL_PRISM
2988     };
2989     int num_new_elements = sizeof(new_elements) / sizeof(int);
2990     int new_element = new_elements[RND(num_new_elements)];
2991
2992     Store[ELX][ELY] = new_element + RND(get_num_elements(new_element));
2993     Feld[ELX][ELY] = EL_GRAY_BALL_OPENING;
2994
2995     /* !!! CHECK AGAIN: Laser on Polarizer !!! */
2996     ScanLaser();
2997
2998     return;
2999
3000 #if 0
3001     int graphic;
3002
3003     switch (RND(5))
3004     {
3005       case 0:
3006         element = EL_MIRROR_START + RND(16);
3007         break;
3008       case 1:
3009         {
3010           int rnd = RND(3);
3011
3012           element = (rnd == 0 ? EL_KETTLE : rnd == 1 ? EL_BOMB : EL_PRISM);
3013         }
3014         break;
3015       default:
3016         {
3017           int rnd = RND(3);
3018
3019           element = (rnd == 0 ? EL_FUSE_ON :
3020                      rnd >= 1 && rnd <= 4 ? EL_PACMAN_RIGHT + rnd - 1 :
3021                      rnd >= 5 && rnd <= 20 ? EL_POLAR_START + rnd - 5 :
3022                      rnd >= 21 && rnd <= 24 ? EL_POLAR_CROSS_START + rnd - 21 :
3023                      EL_MIRROR_FIXED_START + rnd - 25);
3024         }
3025         break;
3026     }
3027
3028     graphic = el2gfx(element);
3029
3030     for(i=0; i<50; i++)
3031     {
3032       int x = RND(26);
3033       int y = RND(26);
3034
3035 #if 0
3036       BlitBitmap(pix[PIX_BACK], drawto,
3037                  SX + (graphic % GFX_PER_LINE) * TILEX + x,
3038                  SY + (graphic / GFX_PER_LINE) * TILEY + y, 6, 6,
3039                  SX + ELX * TILEX + x,
3040                  SY + ELY * TILEY + y);
3041 #endif
3042       MarkTileDirty(ELX, ELY);
3043       BackToFront();
3044
3045       DrawLaser(0, DL_LASER_ENABLED);
3046
3047       Delay(50);
3048     }
3049
3050     Feld[ELX][ELY] = element;
3051     DrawField_MM(ELX, ELY);
3052
3053 #if 0
3054     printf("NEW ELEMENT: (%d, %d)\n", ELX, ELY);
3055 #endif
3056
3057     /* above stuff: GRAY BALL -> PRISM !!! */
3058 /*
3059     LX = ELX * TILEX + 14;
3060     LY = ELY * TILEY + 14;
3061     if (laser.current_angle == (laser.current_angle >> 1) << 1)
3062       OK = 8;
3063     else
3064       OK = 4;
3065     LX -= OK * XS;
3066     LY -= OK * YS;
3067
3068     laser.num_edges -= 2;
3069     laser.num_damages--;
3070 */
3071
3072 #if 0
3073     for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
3074       if (laser.damage[i].is_mirror)
3075         break;
3076
3077     if (i > 0)
3078       DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3079     else
3080       DrawLaser(0, DL_LASER_DISABLED);
3081 #else
3082     DrawLaser(0, DL_LASER_DISABLED);
3083 #endif
3084
3085     ScanLaser();
3086
3087     /*
3088     printf("TEST ELEMENT: %d\n", Feld[0][0]);
3089     */
3090 #endif
3091
3092     return;
3093   }
3094
3095   if (IS_WALL_ICE(element) && CT > 1000)
3096   {
3097     PlaySoundStereo(SND_SLURP, ST(ELX));
3098
3099
3100
3101     {
3102       Feld[ELX][ELY] = Feld[ELX][ELY] - EL_WALL_ICE + EL_WALL_CHANGING;
3103       Store[ELX][ELY] = EL_WALL_ICE;
3104       Store2[ELX][ELY] = laser.wall_mask;
3105
3106       laser.dest_element = Feld[ELX][ELY];
3107
3108       return;
3109     }
3110
3111
3112
3113
3114     for(i=0; i<5; i++)
3115     {
3116       int phase = i + 1;
3117
3118       if (i == 4)
3119       {
3120         Feld[ELX][ELY] &= (laser.wall_mask ^ 0xFF);
3121         phase = 0;
3122       }
3123
3124       DrawWallsAnimation_MM(ELX, ELY, Feld[ELX][ELY], phase, laser.wall_mask);
3125       BackToFront();
3126       Delay(100);
3127     }
3128
3129     if (Feld[ELX][ELY] == EL_WALL_ICE)
3130       Feld[ELX][ELY] = EL_EMPTY;
3131
3132 /*
3133     laser.num_edges--;
3134     LX = laser.edge[laser.num_edges].x - (SX + 2);
3135     LY = laser.edge[laser.num_edges].y - (SY + 2);
3136 */
3137
3138     for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i>=0; i--)
3139       if (laser.damage[i].is_mirror)
3140         break;
3141
3142     if (i > 0)
3143       DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
3144     else
3145       DrawLaser(0, DL_LASER_DISABLED);
3146
3147     ScanLaser();
3148
3149     return;
3150   }
3151
3152   if (IS_WALL_AMOEBA(element) && CT > 1200)
3153   {
3154     int k1, k2, k3, dx, dy, de, dm;
3155     int element2 = Feld[ELX][ELY];
3156
3157     if (element2 != EL_EMPTY && !IS_WALL_AMOEBA(element))
3158       return;
3159
3160     for (i = laser.num_damages - 1; i>=0; i--)
3161       if (laser.damage[i].is_mirror)
3162         break;
3163
3164     r = laser.num_edges;
3165     d = laser.num_damages;
3166     k1 = i;
3167
3168     if (k1 > 0)
3169     {
3170       int x, y;
3171
3172       DrawLaser(laser.damage[k1].edge - 1, DL_LASER_DISABLED);
3173
3174       laser.num_edges++;
3175       DrawLaser(0, DL_LASER_ENABLED);
3176       laser.num_edges--;
3177
3178       x = laser.damage[k1].x;
3179       y = laser.damage[k1].y;
3180       DrawField_MM(x, y);
3181     }
3182
3183     for(i=0; i<4; i++)
3184     {
3185       if (laser.wall_mask & (1 << i))
3186       {
3187         if (ReadPixel(drawto,
3188                       SX + ELX * TILEX + 14 + (i % 2) * 2,
3189                       SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3190           break;
3191         if (ReadPixel(drawto,
3192                       SX + ELX * TILEX + 31 * (i % 2),
3193                       SY + ELY * TILEY + 14 + (i / 2) * 2) == pen_ray)
3194           break;
3195       }
3196     }
3197
3198     k2 = i;
3199
3200     for(i=0; i<4; i++)
3201     {
3202       if (laser.wall_mask & (1 << i))
3203       {
3204         if (ReadPixel(drawto,
3205                       SX + ELX * TILEX + 31 * (i % 2),
3206                       SY + ELY * TILEY + 31 * (i / 2)) == pen_ray)
3207           break;
3208       }
3209     }
3210
3211     k3 = i;
3212
3213     if (laser.num_beamers > 0 ||
3214         k1 < 1 || k2 < 4 || k3 < 4 ||
3215         ReadPixel(drawto, SX + ELX * TILEX + 14, SY + ELY * TILEY + 14)
3216         == pen_ray)
3217     {
3218       laser.num_edges = r;
3219       laser.num_damages = d;
3220       DrawLaser(0, DL_LASER_DISABLED);
3221     }
3222
3223     Feld[ELX][ELY] = element | laser.wall_mask;
3224     dx = ELX;
3225     dy = ELY;
3226     de = Feld[ELX][ELY];
3227     dm = laser.wall_mask;
3228
3229
3230
3231 #if 1
3232     {
3233       int x = ELX, y = ELY;
3234       int wall_mask = laser.wall_mask;
3235
3236
3237       ScanLaser();
3238       DrawLaser(0, DL_LASER_ENABLED);
3239
3240       PlaySoundStereo(SND_AMOEBE, ST(dx));
3241
3242
3243
3244       Feld[x][y] = Feld[x][y] - EL_WALL_AMOEBA + EL_WALL_CHANGING;
3245       Store[x][y] = EL_WALL_AMOEBA;
3246       Store2[x][y] = wall_mask;
3247
3248       return;
3249     }
3250 #endif
3251
3252
3253
3254     DrawWallsAnimation_MM(dx, dy, de, 4, dm);
3255     ScanLaser();
3256     DrawLaser(0, DL_LASER_ENABLED);
3257
3258     PlaySoundStereo(SND_AMOEBE, ST(dx));
3259
3260     for(i=4; i>=0; i--)
3261     {
3262       DrawWallsAnimation_MM(dx, dy, de, i, dm);
3263       BackToFront();
3264       Delay(20);
3265     }
3266
3267     DrawLaser(0, DL_LASER_ENABLED);
3268
3269     return;
3270   }
3271
3272   if ((element == EL_BLOCK_WOOD || element == EL_BLOCK_STONE) &&
3273       laser.stops_inside_element && CT > 1500)
3274   {
3275     int x, y;
3276     int k;
3277
3278     if (ABS(XS) > ABS(YS))
3279       k = 0;
3280     else
3281       k = 1;
3282     if (XS < YS)
3283       k += 2;
3284
3285     for(i=0; i<4; i++)
3286     {
3287       if (i)
3288         k++;
3289       if (k > 3)
3290         k=0;
3291
3292       x = ELX + Step[k * 4].x;
3293       y = ELY + Step[k * 4].y;
3294
3295       if (!IN_LEV_FIELD(x, y) || Feld[x][y] != EL_EMPTY)
3296         continue;
3297
3298       if (ObjHit(x, y, HIT_POS_CENTER | HIT_POS_EDGE | HIT_POS_BETWEEN))
3299         continue;
3300
3301       break;
3302     }
3303
3304     if (i > 3)
3305     {
3306       laser.overloaded = (element == EL_BLOCK_STONE);
3307       return;
3308     }
3309
3310     PlaySoundStereo(SND_BONG, ST(ELX));
3311
3312     Feld[ELX][ELY] = 0;
3313     Feld[x][y] = element;
3314
3315     DrawGraphic_MM(ELX, ELY, -1);
3316     DrawField_MM(x, y);
3317
3318     if (element == EL_BLOCK_STONE && Box[ELX][ELY])
3319     {
3320       DrawLaser(Box[ELX][ELY] - 1, DL_LASER_DISABLED);
3321       DrawLaser(laser.num_edges - 1, DL_LASER_ENABLED);
3322     }
3323
3324     ScanLaser();
3325
3326     return;
3327   }
3328
3329   if (element == EL_FUEL_FULL && CT > 200)
3330   {
3331     for(i=game_mm.energy_left; i<=MAX_LASER_ENERGY; i+=2)
3332     {
3333 #if 0
3334       BlitBitmap(pix[PIX_DOOR], drawto,
3335                  DOOR_GFX_PAGEX4 + XX_ENERGY,
3336                  DOOR_GFX_PAGEY1 + YY_ENERGY + ENERGY_YSIZE - i,
3337                  ENERGY_XSIZE, i, DX_ENERGY,
3338                  DY_ENERGY + ENERGY_YSIZE - i);
3339 #endif
3340
3341       redraw_mask |= REDRAW_DOOR_1;
3342       BackToFront();
3343
3344       Delay(20);
3345     }
3346
3347     game_mm.energy_left = MAX_LASER_ENERGY;
3348     Feld[ELX][ELY] = EL_FUEL_EMPTY;
3349     DrawField_MM(ELX, ELY);
3350
3351     DrawLaser(0, DL_LASER_ENABLED);
3352
3353     return;
3354   }
3355
3356   return;
3357 }
3358
3359 void MovePacMen()
3360 {
3361   static int p = -1;
3362   int mx, my, ox, oy, nx, ny;
3363   int g, element;
3364   int l;
3365
3366   if (++p >= game_mm.num_pacman)
3367     p = 0;
3368   game_mm.pacman[p].dir--;
3369
3370   for(l=1; l<5; l++)
3371   {
3372     game_mm.pacman[p].dir++;
3373
3374     if (game_mm.pacman[p].dir > 4)
3375       game_mm.pacman[p].dir = 1;
3376
3377     if (game_mm.pacman[p].dir % 2)
3378     {
3379       mx = 0;
3380       my = game_mm.pacman[p].dir - 2;
3381     }
3382     else
3383     {
3384       my = 0;
3385       mx = 3 - game_mm.pacman[p].dir;
3386     }
3387
3388     ox = game_mm.pacman[p].x;
3389     oy = game_mm.pacman[p].y;
3390     nx = ox + mx;
3391     ny = oy + my;
3392     element = Feld[nx][ny];
3393     if (nx < 0 || nx > 15 || ny < 0 || ny > 11)
3394       continue;
3395
3396     if (!IS_EATABLE4PACMAN(element))
3397       continue;
3398
3399     if (ObjHit(nx, ny, HIT_POS_CENTER))
3400       continue;
3401
3402     Feld[ox][oy] = EL_EMPTY;
3403     Feld[nx][ny] =
3404       EL_PACMAN_RIGHT - 1 +
3405       (game_mm.pacman[p].dir - 1 +
3406        (game_mm.pacman[p].dir % 2) * 2);
3407
3408     game_mm.pacman[p].x = nx;
3409     game_mm.pacman[p].y = ny;
3410     g = Feld[nx][ny] - EL_PACMAN_RIGHT;
3411     DrawGraphic_MM(ox, oy, IMG_EMPTY);
3412
3413     if (element != EL_EMPTY)
3414     {
3415       int i;
3416
3417       CT = Counter();
3418       ox = SX + ox * TILEX;
3419       oy = SY + oy * TILEY;
3420
3421       for(i=1; i<33; i+=2)
3422       {
3423 #if 1
3424         // !!! temporary fix to compile -- change to game graphics !!!
3425         BlitBitmap(drawto, window,
3426                    SX + g * TILEX, SY + 4 * TILEY, TILEX, TILEY,
3427                    ox + i * mx, oy + i * my);
3428 #else
3429         BlitBitmap(pix[PIX_BACK], window,
3430                    SX + g * TILEX, SY + 4 * TILEY, TILEX, TILEY,
3431                    ox + i * mx, oy + i * my);
3432 #endif
3433       }
3434       Ct = Ct + Counter() - CT;
3435     }
3436     DrawField_MM(nx, ny);
3437     BackToFront();
3438
3439     if (!laser.fuse_off)
3440     {
3441       DrawLaser(0, DL_LASER_ENABLED);
3442
3443       if (ObjHit(nx, ny, HIT_POS_BETWEEN))
3444       {
3445         AddDamagedField(nx, ny);
3446         laser.damage[laser.num_damages - 1].edge = 0;
3447       }
3448     }
3449
3450     if (element == EL_BOMB)
3451     {
3452       DeletePacMan(nx, ny);
3453     }
3454
3455     if (IS_WALL_AMOEBA(element) &&
3456         (LX + 2 * XS) / TILEX == nx &&
3457         (LY + 2 * YS) / TILEY == ny)
3458     {
3459       laser.num_edges--;
3460       ScanLaser();
3461     }
3462     break;
3463   }
3464 }
3465
3466 void GameWon_MM()
3467 {
3468   int hi_pos;
3469   boolean raise_level = FALSE;
3470
3471 #if 0
3472   if (local_player->MovPos)
3473     return;
3474
3475   local_player->LevelSolved = FALSE;
3476 #endif
3477
3478   if (game_mm.energy_left)
3479   {
3480     if (setup.sound_loops)
3481       PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT, SND_CTRL_PLAY_LOOP);
3482
3483     while(game_mm.energy_left > 0)
3484     {
3485       if (!setup.sound_loops)
3486         PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3487
3488       /*
3489       if (game_mm.energy_left > 0 && !(game_mm.energy_left % 10))
3490         RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3491       */
3492
3493       RaiseScore_MM(5);
3494
3495       game_mm.energy_left--;
3496       if (game_mm.energy_left >= 0)
3497       {
3498 #if 0
3499         BlitBitmap(pix[PIX_DOOR], drawto,
3500                    DOOR_GFX_PAGEX5 + XX_ENERGY, DOOR_GFX_PAGEY1 + YY_ENERGY,
3501                    ENERGY_XSIZE, ENERGY_YSIZE - game_mm.energy_left,
3502                    DX_ENERGY, DY_ENERGY);
3503 #endif
3504         redraw_mask |= REDRAW_DOOR_1;
3505       }
3506
3507       BackToFront();
3508       Delay(10);
3509     }
3510
3511     if (setup.sound_loops)
3512       StopSound(SND_SIRR);
3513   }
3514   else if (native_mm_level.time == 0)           /* level without time limit */
3515   {
3516     if (setup.sound_loops)
3517       PlaySoundExt(SND_SIRR, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT, SND_CTRL_PLAY_LOOP);
3518
3519     while(TimePlayed < 999)
3520     {
3521       if (!setup.sound_loops)
3522         PlaySoundStereo(SND_SIRR, SOUND_MAX_RIGHT);
3523       if (TimePlayed < 999 && !(TimePlayed % 10))
3524         RaiseScore_MM(native_mm_level.score[SC_ZEITBONUS]);
3525       if (TimePlayed < 900 && !(TimePlayed % 10))
3526         TimePlayed += 10;
3527       else
3528         TimePlayed++;
3529
3530       /*
3531       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
3532       */
3533
3534       BackToFront();
3535       Delay(10);
3536     }
3537
3538     if (setup.sound_loops)
3539       StopSound(SND_SIRR);
3540   }
3541
3542 #if 0
3543   FadeSounds();
3544 #endif
3545
3546   CloseDoor(DOOR_CLOSE_1);
3547
3548   Request("Level solved !", REQ_CONFIRM);
3549
3550   if (level_nr == leveldir_current->handicap_level)
3551   {
3552     leveldir_current->handicap_level++;
3553     SaveLevelSetup_SeriesInfo();
3554   }
3555
3556   if (level_editor_test_game)
3557     game_mm.score = -1;         /* no highscore when playing from editor */
3558   else if (level_nr < leveldir_current->last_level)
3559     raise_level = TRUE;         /* advance to next level */
3560
3561   if ((hi_pos = NewHiScore_MM()) >= 0)
3562   {
3563     game_status = HALLOFFAME;
3564     // DrawHallOfFame(hi_pos);
3565     if (raise_level)
3566       level_nr++;
3567   }
3568   else
3569   {
3570     game_status = MAINMENU;
3571     if (raise_level)
3572       level_nr++;
3573     // DrawMainMenu();
3574   }
3575
3576   BackToFront();
3577 }
3578
3579 int NewHiScore_MM()
3580 {
3581   int k, l;
3582   int position = -1;
3583
3584   // LoadScore(level_nr);
3585
3586   if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
3587       game_mm.score < highscore[MAX_SCORE_ENTRIES - 1].Score)
3588     return -1;
3589
3590   for (k=0; k<MAX_SCORE_ENTRIES; k++)
3591   {
3592     if (game_mm.score > highscore[k].Score)
3593     {
3594       /* player has made it to the hall of fame */
3595
3596       if (k < MAX_SCORE_ENTRIES - 1)
3597       {
3598         int m = MAX_SCORE_ENTRIES - 1;
3599
3600 #ifdef ONE_PER_NAME
3601         for (l=k; l<MAX_SCORE_ENTRIES; l++)
3602           if (!strcmp(setup.player_name, highscore[l].Name))
3603             m = l;
3604         if (m == k)     /* player's new highscore overwrites his old one */
3605           goto put_into_list;
3606 #endif
3607
3608         for (l=m; l>k; l--)
3609         {
3610           strcpy(highscore[l].Name, highscore[l - 1].Name);
3611           highscore[l].Score = highscore[l - 1].Score;
3612         }
3613       }
3614
3615 #ifdef ONE_PER_NAME
3616       put_into_list:
3617 #endif
3618       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3619       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3620       highscore[k].Score = game_mm.score;
3621       position = k;
3622       break;
3623     }
3624
3625 #ifdef ONE_PER_NAME
3626     else if (!strncmp(setup.player_name, highscore[k].Name,
3627                       MAX_PLAYER_NAME_LEN))
3628       break;    /* player already there with a higher score */
3629 #endif
3630
3631   }
3632
3633   // if (position >= 0)
3634   //   SaveScore(level_nr);
3635
3636   return position;
3637 }
3638
3639 static void InitMovingField_MM(int x, int y, int direction)
3640 {
3641   int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3642   int newy = y + (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3643
3644   MovDir[x][y] = direction;
3645   MovDir[newx][newy] = direction;
3646   if (Feld[newx][newy] == EL_EMPTY)
3647     Feld[newx][newy] = EL_BLOCKED;
3648 }
3649
3650 static void Moving2Blocked_MM(int x, int y, int *goes_to_x, int *goes_to_y)
3651 {
3652   int direction = MovDir[x][y];
3653   int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3654   int newy = y + (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3655
3656   *goes_to_x = newx;
3657   *goes_to_y = newy;
3658 }
3659
3660 static void Blocked2Moving_MM(int x, int y,
3661                               int *comes_from_x, int *comes_from_y)
3662 {
3663   int oldx = x, oldy = y;
3664   int direction = MovDir[x][y];
3665
3666   if (direction == MV_LEFT)
3667     oldx++;
3668   else if (direction == MV_RIGHT)
3669     oldx--;
3670   else if (direction == MV_UP)
3671     oldy++;
3672   else if (direction == MV_DOWN)
3673     oldy--;
3674
3675   *comes_from_x = oldx;
3676   *comes_from_y = oldy;
3677 }
3678
3679 static int MovingOrBlocked2Element_MM(int x, int y)
3680 {
3681   int element = Feld[x][y];
3682
3683   if (element == EL_BLOCKED)
3684   {
3685     int oldx, oldy;
3686
3687     Blocked2Moving_MM(x, y, &oldx, &oldy);
3688     return Feld[oldx][oldy];
3689   }
3690   else
3691     return element;
3692 }
3693
3694 #if 0
3695 static void RemoveField(int x, int y)
3696 {
3697   Feld[x][y] = EL_EMPTY;
3698   MovPos[x][y] = 0;
3699   MovDir[x][y] = 0;
3700   MovDelay[x][y] = 0;
3701 }
3702 #endif
3703
3704 static void RemoveMovingField_MM(int x, int y)
3705 {
3706   int oldx = x, oldy = y, newx = x, newy = y;
3707
3708   if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
3709     return;
3710
3711   if (IS_MOVING(x, y))
3712   {
3713     Moving2Blocked_MM(x, y, &newx, &newy);
3714     if (Feld[newx][newy] != EL_BLOCKED)
3715       return;
3716   }
3717   else if (Feld[x][y] == EL_BLOCKED)
3718   {
3719     Blocked2Moving_MM(x, y, &oldx, &oldy);
3720     if (!IS_MOVING(oldx, oldy))
3721       return;
3722   }
3723
3724   Feld[oldx][oldy] = EL_EMPTY;
3725   Feld[newx][newy] = EL_EMPTY;
3726   MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
3727   MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
3728
3729   DrawLevelField_MM(oldx, oldy);
3730   DrawLevelField_MM(newx, newy);
3731 }
3732
3733 void PlaySoundLevel(int x, int y, int sound_nr)
3734 {
3735   int sx = SCREENX(x), sy = SCREENY(y);
3736   int volume, stereo;
3737   int silence_distance = 8;
3738
3739   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
3740       (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
3741     return;
3742
3743   if (!IN_LEV_FIELD(x, y) ||
3744       sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
3745       sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
3746     return;
3747
3748   volume = SOUND_MAX_VOLUME;
3749
3750 #ifndef MSDOS
3751   stereo = (sx - SCR_FIELDX/2) * 12;
3752 #else
3753   stereo = SOUND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
3754   if (stereo > SOUND_MAX_RIGHT)
3755     stereo = SOUND_MAX_RIGHT;
3756   if (stereo < SOUND_MAX_LEFT)
3757     stereo = SOUND_MAX_LEFT;
3758 #endif
3759
3760   if (!IN_SCR_FIELD(sx, sy))
3761   {
3762     int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
3763     int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
3764
3765     volume -= volume * (dx > dy ? dx : dy) / silence_distance;
3766   }
3767
3768   PlaySoundExt(sound_nr, volume, stereo, SND_CTRL_PLAY_SOUND);
3769 }
3770
3771 static void RaiseScore_MM(int value)
3772 {
3773   game_mm.score += value;
3774 #if 0
3775   DrawText(DX_SCORE, DY_SCORE, int2str(game_mm.score, 4),
3776            FONT_TEXT_2);
3777 #endif
3778 }
3779
3780 void RaiseScoreElement_MM(int element)
3781 {
3782   switch(element)
3783   {
3784     case EL_PACMAN:
3785       RaiseScore_MM(native_mm_level.score[SC_PACMAN]);
3786       break;
3787     case EL_KEY:
3788       RaiseScore_MM(native_mm_level.score[SC_KEY]);
3789       break;
3790     default:
3791       break;
3792   }
3793 }