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