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