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