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