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