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