changed simple "printf()" debug output to new debug log function
[rocksndiamonds.git] / src / game_mm / mm_tools.c
1 // ============================================================================
2 // Mirror Magic -- McDuffin's Revenge
3 // ----------------------------------------------------------------------------
4 // (c) 1994-2017 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // mm_tools.c
10 // ============================================================================
11
12 #include "main_mm.h"
13
14 #include "mm_main.h"
15 #include "mm_tools.h"
16
17
18 void SetDrawtoField_MM(int mode)
19 {
20   int full_xsize = lev_fieldx * TILESIZE_VAR;
21   int full_ysize = lev_fieldy * TILESIZE_VAR;
22
23   // distance (delta) from screen border (SX/SY) to centered level playfield
24   dSX = (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
25   dSY = (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
26
27   // for convenience, absolute screen position to centered level playfield
28   cSX = SX + dSX;
29   cSY = SY + dSY;
30   cSX2 = SX + dSX + 2;  // including playfield border
31   cSY2 = SY + dSY + 2;  // including playfield border
32
33   if (mode == DRAW_TO_BACKBUFFER)
34   {
35     cFX = FX + dSX;
36     cFY = FY + dSY;
37   }
38
39   SetTileCursorSXSY(cSX, cSY);
40 }
41
42 void ClearWindow(void)
43 {
44   ClearRectangle(backbuffer, REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
45
46   SetDrawtoField(DRAW_TO_BACKBUFFER);
47   SetDrawtoField_MM(DRAW_TO_BACKBUFFER);
48
49   redraw_mask |= REDRAW_FIELD;
50 }
51
52 void DrawGraphicAnimation_MM(int x, int y, int graphic, int frame)
53 {
54   Bitmap *bitmap;
55   int src_x, src_y;
56
57   getGraphicSource(graphic, frame, &bitmap, &src_x, &src_y);
58
59   BlitBitmap(bitmap, drawto_field, src_x, src_y, TILEX, TILEY,
60              cFX + x * TILEX, cFY + y * TILEY);
61 }
62
63 void DrawGraphic_MM(int x, int y, int graphic)
64 {
65 #if DEBUG
66   if (!IN_SCR_FIELD(x,y))
67   {
68     Debug("game:mm:DrawGraphic_MM", "x = %d, y = %d, graphic = %d",
69           x, y, graphic);
70     Debug("game:mm:DrawGraphic_MM", "This should never happen!");
71
72     return;
73   }
74 #endif
75
76   DrawGraphicExt_MM(drawto_field, cFX + x * TILEX, cFY + y * TILEY, graphic);
77
78   MarkTileDirty(x, y);
79 }
80
81 void DrawGraphicExt_MM(DrawBuffer *d, int x, int y, int graphic)
82 {
83   Bitmap *bitmap;
84   int src_x, src_y;
85
86   getGraphicSource(graphic, 0, &bitmap, &src_x, &src_y);
87
88   BlitBitmap(bitmap, d, src_x, src_y, TILEX, TILEY, x, y);
89 }
90
91 void DrawGraphicThruMask_MM(int x, int y, int graphic)
92 {
93 #if DEBUG
94   if (!IN_SCR_FIELD(x,y))
95   {
96     Debug("game:mm:DrawGraphicThruMask_MM", "x = %d,y = %d, graphic = %d",
97           x, y, graphic);
98     Debug("game:mm:DrawGraphicThruMask_MM", "This should never happen!");
99
100     return;
101   }
102 #endif
103
104   DrawGraphicThruMaskExt_MM(drawto_field, cFX + x * TILEX, cFY + y * TILEY,
105                             graphic);
106
107   MarkTileDirty(x,y);
108 }
109
110 void DrawGraphicThruMaskExt_MM(DrawBuffer *d, int dest_x, int dest_y,
111                                int graphic)
112 {
113   int src_x, src_y;
114   Bitmap *src_bitmap;
115
116   if (graphic == IMG_EMPTY)
117     return;
118
119   getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
120
121   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY, dest_x, dest_y);
122 }
123
124 void DrawMiniGraphic_MM(int x, int y, int graphic)
125 {
126   DrawMiniGraphicExt_MM(drawto, cSX + x * MINI_TILEX, cSY + y * MINI_TILEY,
127                         graphic);
128
129   MarkTileDirty(x / 2, y / 2);
130 }
131
132 #if 0
133 static void getMicroGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
134 {
135   getSizedGraphicSource(graphic, 0, TILESIZE / 4, bitmap, x, y);
136 }
137 #endif
138
139 void DrawMiniGraphicExt_MM(DrawBuffer *d, int x, int y, int graphic)
140 {
141   Bitmap *bitmap;
142   int src_x, src_y;
143
144   getMiniGraphicSource(graphic, &bitmap, &src_x, &src_y);
145
146   BlitBitmap(bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
147 }
148
149 void DrawGraphicShifted_MM(int x,int y, int dx,int dy, int graphic,
150                         int cut_mode, int mask_mode)
151 {
152   int width = TILEX, height = TILEY;
153   int cx = 0, cy = 0;
154   int src_x, src_y, dest_x, dest_y;
155   Bitmap *src_bitmap;
156
157   if (graphic < 0)
158   {
159     DrawGraphic_MM(x, y, graphic);
160
161     return;
162   }
163
164   if (dx || dy)                 // Verschiebung der Grafik?
165   {
166     if (x < BX1)                // Element kommt von links ins Bild
167     {
168       x = BX1;
169       width = dx;
170       cx = TILEX - dx;
171       dx = 0;
172     }
173     else if (x > BX2)           // Element kommt von rechts ins Bild
174     {
175       x = BX2;
176       width = -dx;
177       dx = TILEX + dx;
178     }
179     else if (x==BX1 && dx < 0)  // Element verläßt links das Bild
180     {
181       width += dx;
182       cx = -dx;
183       dx = 0;
184     }
185     else if (x==BX2 && dx > 0)  // Element verläßt rechts das Bild
186       width -= dx;
187     else if (dx)                // allg. Bewegung in x-Richtung
188       MarkTileDirty(x + SIGN(dx), y);
189
190     if (y < BY1)                // Element kommt von oben ins Bild
191     {
192       if (cut_mode==CUT_BELOW)  // Element oberhalb des Bildes
193         return;
194
195       y = BY1;
196       height = dy;
197       cy = TILEY - dy;
198       dy = 0;
199     }
200     else if (y > BY2)           // Element kommt von unten ins Bild
201     {
202       y = BY2;
203       height = -dy;
204       dy = TILEY + dy;
205     }
206     else if (y==BY1 && dy < 0)  // Element verläßt oben das Bild
207     {
208       height += dy;
209       cy = -dy;
210       dy = 0;
211     }
212     else if (dy > 0 && cut_mode == CUT_ABOVE)
213     {
214       if (y == BY2)             // Element unterhalb des Bildes
215         return;
216
217       height = dy;
218       cy = TILEY - dy;
219       dy = TILEY;
220       MarkTileDirty(x, y + 1);
221     }                           // Element verläßt unten das Bild
222     else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
223     {
224       height -= dy;
225     }
226     else if (dy)                // allg. Bewegung in y-Richtung
227     {
228       MarkTileDirty(x, y + SIGN(dy));
229     }
230   }
231
232   getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
233
234   src_x += cx;
235   src_y += cy;
236
237   dest_x = cFX + x * TILEX + dx;
238   dest_y = cFY + y * TILEY + dy;
239
240 #if DEBUG
241   if (!IN_SCR_FIELD(x,y))
242   {
243     Debug("game:mm:DrawGraphicShifted_MM", "x = %d, y = %d, graphic = %d",
244           x, y, graphic);
245     Debug("game:mm:DrawGraphicShifted_MM", "This should never happen!");
246
247     return;
248   }
249 #endif
250
251   if (mask_mode == USE_MASKING)
252     BlitBitmapMasked(src_bitmap, drawto_field,
253                      src_x, src_y, TILEX, TILEY, dest_x, dest_y);
254   else
255     BlitBitmap(src_bitmap, drawto_field,
256                src_x, src_y, width, height, dest_x, dest_y);
257
258   MarkTileDirty(x,y);
259 }
260
261 void DrawGraphicShiftedThruMask_MM(int x,int y, int dx,int dy, int graphic,
262                                 int cut_mode)
263 {
264   DrawGraphicShifted_MM(x, y, dx, dy, graphic, cut_mode, USE_MASKING);
265 }
266
267 void DrawScreenElementExt_MM(int x, int y, int dx, int dy, int element,
268                           int cut_mode, int mask_mode)
269 {
270   int ux = LEVELX(x), uy = LEVELY(y);
271   int graphic = el2gfx(element);
272   int phase8 = ABS(MovPos[ux][uy]) / (TILEX / 8);
273   int phase2  = phase8 / 4;
274   int dir = MovDir[ux][uy];
275
276   if (element == EL_PACMAN)
277   {
278     graphic = (phase2 ? IMG_MM_PACMAN_RIGHT : IMG_MM_PACMAN_EATING_RIGHT);
279
280     if (dir == MV_UP)
281       graphic += 1;
282     else if (dir == MV_LEFT)
283       graphic += 2;
284     else if (dir == MV_DOWN)
285       graphic += 3;
286   }
287
288   if (dx || dy)
289     DrawGraphicShifted_MM(x, y, dx, dy, graphic, cut_mode, mask_mode);
290   else if (mask_mode == USE_MASKING)
291     DrawGraphicThruMask_MM(x, y, graphic);
292   else
293     DrawGraphic_MM(x, y, graphic);
294 }
295
296 void DrawLevelElementExt_MM(int x, int y, int dx, int dy, int element,
297                          int cut_mode, int mask_mode)
298 {
299   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
300     DrawScreenElementExt_MM(SCREENX(x), SCREENY(y), dx, dy, element,
301                          cut_mode, mask_mode);
302 }
303
304 void DrawScreenElementShifted_MM(int x, int y, int dx, int dy, int element,
305                               int cut_mode)
306 {
307   DrawScreenElementExt_MM(x, y, dx, dy, element, cut_mode, NO_MASKING);
308 }
309
310 void DrawLevelElementShifted_MM(int x, int y, int dx, int dy, int element,
311                              int cut_mode)
312 {
313   DrawLevelElementExt_MM(x, y, dx, dy, element, cut_mode, NO_MASKING);
314 }
315
316 void DrawScreenElementThruMask_MM(int x, int y, int element)
317 {
318   DrawScreenElementExt_MM(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
319 }
320
321 void DrawLevelElementThruMask_MM(int x, int y, int element)
322 {
323   DrawLevelElementExt_MM(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
324 }
325
326 void DrawLevelFieldThruMask_MM(int x, int y)
327 {
328   DrawLevelElementExt_MM(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
329 }
330
331 void DrawScreenElement_MM(int x, int y, int element)
332 {
333   DrawScreenElementExt_MM(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
334 }
335
336 void DrawLevelElement_MM(int x, int y, int element)
337 {
338   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
339     DrawScreenElement_MM(SCREENX(x), SCREENY(y), element);
340 }
341
342 void DrawScreenField_MM(int x, int y)
343 {
344   int element = Tile[x][y];
345
346   if (!IN_LEV_FIELD(x, y))
347     return;
348
349   if (IS_MOVING(x, y))
350   {
351     int horiz_move = (MovDir[x][y] == MV_LEFT || MovDir[x][y] == MV_RIGHT);
352
353     DrawScreenElement_MM(x, y, EL_EMPTY);
354
355     if (horiz_move)
356       DrawScreenElementShifted_MM(x, y, MovPos[x][y], 0, element, NO_CUTTING);
357     else
358       DrawScreenElementShifted_MM(x, y, 0, MovPos[x][y], element, NO_CUTTING);
359   }
360   else if (IS_BLOCKED(x, y))
361   {
362     int oldx, oldy;
363     int sx, sy;
364     int horiz_move;
365
366     Blocked2Moving(x, y, &oldx, &oldy);
367
368     sx = SCREENX(oldx);
369     sy = SCREENY(oldy);
370     horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
371                   MovDir[oldx][oldy] == MV_RIGHT);
372
373     DrawScreenElement_MM(x, y, EL_EMPTY);
374
375     element = Tile[oldx][oldy];
376
377     if (horiz_move)
378       DrawScreenElementShifted_MM(sx, sy, MovPos[oldx][oldy], 0, element,
379                                   NO_CUTTING);
380     else
381       DrawScreenElementShifted_MM(sx, sy, 0, MovPos[oldx][oldy], element,
382                                   NO_CUTTING);
383   }
384   else if (IS_DRAWABLE(element))
385   {
386     DrawScreenElement_MM(x, y, element);
387   }
388   else
389   {
390     DrawScreenElement_MM(x, y, EL_EMPTY);
391   }
392 }
393
394 void DrawLevelField_MM(int x, int y)
395 {
396   DrawScreenField_MM(x, y);
397 }
398
399 void DrawMiniElement_MM(int x, int y, int element)
400 {
401   int graphic;
402
403   if (!element)
404   {
405     DrawMiniGraphic_MM(x, y, IMG_EMPTY);
406
407     return;
408   }
409
410   graphic = el2gfx(element);
411
412   DrawMiniGraphic_MM(x, y, graphic);
413 }
414
415 void DrawMiniElementOrWall_MM(int sx, int sy, int scroll_x, int scroll_y)
416 {
417   int x = sx + scroll_x, y = sy + scroll_y;
418
419   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
420     DrawMiniElement_MM(sx, sy, EL_EMPTY);
421   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
422     DrawMiniElement_MM(sx, sy, Tile[x][y]);
423 }
424
425 void DrawField_MM(int x, int y)
426 {
427   int element = Tile[x][y];
428
429   DrawElement_MM(x, y, element);
430 }
431
432 void DrawLevel_MM(void)
433 {
434   int x,y;
435
436   ClearWindow();
437
438   for (x = 0; x < lev_fieldx; x++)
439     for (y = 0; y < lev_fieldy; y++)
440       DrawField_MM(x, y);
441
442   redraw_mask |= REDRAW_FIELD;
443 }
444
445 void DrawWallsExt_MM(int x, int y, int element, int draw_mask)
446 {
447   Bitmap *bitmap;
448   int graphic = el2gfx(WALL_BASE(element));
449   int gx, gy;
450   int i;
451
452   getMiniGraphicSource(graphic, &bitmap, &gx, &gy);
453
454   DrawGraphic_MM(x, y, IMG_EMPTY);
455
456   /*
457   if (IS_WALL_WOOD(element) || IS_WALL_AMOEBA(element) ||
458       IS_DF_WALL_WOOD(element))
459     gx += MINI_TILEX;
460   if (IS_WALL_ICE(element) || IS_WALL_AMOEBA(element))
461     gy += MINI_TILEY;
462   */
463
464   for (i = 0; i < 4; i++)
465   {
466     int dest_x = cSX + x * TILEX + MINI_TILEX * (i % 2);
467     int dest_y = cSY + y * TILEY + MINI_TILEY * (i / 2);
468
469     if (!((1 << i) & draw_mask))
470       continue;
471
472     if (element & (1 << i))
473       BlitBitmap(bitmap, drawto, gx, gy, MINI_TILEX, MINI_TILEY,
474                  dest_x, dest_y);
475     else
476       ClearRectangle(drawto, dest_x, dest_y, MINI_TILEX, MINI_TILEY);
477   }
478
479   MarkTileDirty(x, y);
480 }
481
482 void DrawWalls_MM(int x, int y, int element)
483 {
484   DrawWallsExt_MM(x, y, element, HIT_MASK_ALL);
485 }
486
487 void DrawWallsAnimation_MM(int x, int y, int element, int phase, int bit_mask)
488 {
489   int i;
490
491   if (phase == 0)
492   {
493     DrawWalls_MM(x, y, element);
494
495     return;
496   }
497
498   for (i = 0; i < 4; i++)
499   {
500     if (element & (1 << i))
501     {
502       int graphic;
503       int frame;
504       Bitmap *bitmap;
505       int src_x, src_y;
506       int dst_x = cSX + x * TILEX + (i % 2) * MINI_TILEX;
507       int dst_y = cSY + y * TILEY + (i / 2) * MINI_TILEY;
508
509       if (bit_mask & (1 << i))
510       {
511         graphic = (IS_WALL_AMOEBA(element) ?
512                    IMG_MM_AMOEBA_WALL_GROWING :
513                    IMG_MM_ICE_WALL_SHRINKING);
514         frame = phase;
515       }
516       else
517       {
518         graphic = (IS_WALL_AMOEBA(element) ?
519                    IMG_MM_AMOEBA_WALL :
520                    IMG_MM_ICE_WALL);
521         frame = 0;
522       }
523
524       getSizedGraphicSource(graphic, frame, MINI_TILESIZE, &bitmap,
525                             &src_x, &src_y);
526
527       BlitBitmap(bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
528                  dst_x, dst_y);
529     }
530   }
531
532   MarkTileDirty(x, y);
533 }
534
535 void DrawElement_MM(int x, int y, int element)
536 {
537   if (element == EL_EMPTY)
538     DrawGraphic_MM(x, y, IMG_EMPTY);
539   else if (IS_WALL(element))
540     DrawWalls_MM(x, y, element);
541 #if 0
542   else if (IS_WALL_CHANGING(element) && IS_WALL_CHANGING(Tile[x][y]))
543   {
544     int wall_element = Tile[x][y] - EL_WALL_CHANGING + Store[x][y];
545
546     DrawWalls_MM(x, y, wall_element);
547   }
548 #endif
549   else if (element == EL_PACMAN)
550     DrawLevelField_MM(x, y);
551   else if (element == EL_FUSE_ON &&
552            laser.fuse_off &&
553            laser.fuse_x == x &&
554            laser.fuse_y == y)
555     DrawGraphic_MM(x, y, IMG_MM_FUSE);
556   else
557     DrawGraphic_MM(x, y, el2gfx(element));
558 }
559
560 #if 0
561 static void DrawMicroWalls_MM(int x, int y, int element)
562 {
563   Bitmap *bitmap;
564   int graphic = el2gfx(WALL_BASE(element));
565   int gx, gy;
566   int i;
567
568   getMicroGraphicSource(graphic, &bitmap, &gx, &gy);
569
570   for (i = 0; i < 4; i++)
571   {
572     int xpos = MICROLEV_XPOS + x * MICRO_TILEX + MICRO_WALLX * (i % 2);
573     int ypos = MICROLEV_YPOS + y * MICRO_TILEY + MICRO_WALLY * (i / 2);
574
575     if (element & (1 << i))
576       BlitBitmap(bitmap, drawto, gx, gy, MICRO_WALLX, MICRO_WALLY, xpos, ypos);
577     else
578       ClearRectangle(drawto, xpos, ypos, MICRO_WALLX, MICRO_WALLY);
579   }
580 }
581
582 static void DrawMicroElement_MM(int x, int y, int element)
583 {
584   Bitmap *bitmap;
585   int graphic = el2gfx(element);
586   int gx, gy;
587
588   if (element == EL_EMPTY)
589     return;
590
591   if (IS_WALL(element))
592   {
593     DrawMicroWalls_MM(x, y, element);
594
595     return;
596   }
597
598   getMicroGraphicSource(graphic, &bitmap, &gx, &gy);
599
600   BlitBitmap(bitmap, drawto, gx, gy, MICRO_TILEX, MICRO_TILEY,
601              MICROLEV_XPOS + x * MICRO_TILEX, MICROLEV_YPOS + y * MICRO_TILEY);
602 }
603
604 static void DrawMicroLevelExt_MM(int xpos, int ypos)
605 {
606   int x, y;
607
608   ClearRectangle(drawto, xpos, ypos, MICROLEV_XSIZE, MICROLEV_YSIZE);
609
610   for (x = 0; x < STD_LEV_FIELDX; x++)
611     for (y = 0; y < STD_LEV_FIELDY; y++)
612       DrawMicroElement_MM(x, y, Ur[x][y]);
613
614   redraw_mask |= REDRAW_FIELD;
615 }
616 #endif
617
618 void DrawMiniLevel_MM(int size_x, int size_y, int scroll_x, int scroll_y)
619 {
620   int x, y;
621
622   for (x = 0; x < size_x; x++)
623     for (y = 0; y < size_y; y++)
624       DrawMiniElementOrWall_MM(x, y, scroll_x, scroll_y);
625
626   redraw_mask |= REDRAW_FIELD;
627 }
628
629 #if 0
630 static int REQ_in_range(int x, int y)
631 {
632   if (y > DY + 249 && y < DY + 278)
633   {
634     if (x > DX + 1 && x < DX + 48)
635       return 1;
636     else if (x > DX + 51 && x < DX + 98)
637       return 2;
638   }
639
640   return 0;
641 }
642 #endif
643
644 Pixel ReadPixel(DrawBuffer *bitmap, int x, int y)
645 {
646   return GetPixel(bitmap, x, y);
647 }
648
649 void SetRGB(unsigned int pixel,
650             unsigned short red, unsigned short green, unsigned short blue)
651 {
652 }
653
654 int get_base_element(int element)
655 {
656   if (IS_MIRROR(element))
657     return EL_MIRROR_START;
658   else if (IS_MIRROR_FIXED(element))
659     return EL_MIRROR_FIXED_START;
660   else if (IS_POLAR(element))
661     return EL_POLAR_START;
662   else if (IS_POLAR_CROSS(element))
663     return EL_POLAR_CROSS_START;
664   else if (IS_BEAMER(element))
665     return EL_BEAMER_RED_START + BEAMER_NR(element) * 16;
666   else if (IS_FIBRE_OPTIC(element))
667     return EL_FIBRE_OPTIC_START + FIBRE_OPTIC_NR(element) * 2;
668   else if (IS_MCDUFFIN(element))
669     return EL_MCDUFFIN_START;
670   else if (IS_LASER(element))
671     return EL_LASER_START;
672   else if (IS_RECEIVER(element))
673     return EL_RECEIVER_START;
674   else if (IS_DF_MIRROR(element))
675     return EL_DF_MIRROR_START;
676   else if (IS_DF_MIRROR_AUTO(element))
677     return EL_DF_MIRROR_AUTO_START;
678   else if (IS_PACMAN(element))
679     return EL_PACMAN_START;
680   else if (IS_GRID_STEEL(element))
681     return EL_GRID_STEEL_START;
682   else if (IS_GRID_WOOD(element))
683     return EL_GRID_WOOD_START;
684   else if (IS_GRID_STEEL_FIXED(element))
685     return EL_GRID_STEEL_FIXED_START;
686   else if (IS_GRID_WOOD_FIXED(element))
687     return EL_GRID_WOOD_FIXED_START;
688   else if (IS_GRID_STEEL_AUTO(element))
689     return EL_GRID_STEEL_AUTO_START;
690   else if (IS_GRID_WOOD_AUTO(element))
691     return EL_GRID_WOOD_AUTO_START;
692   else if (IS_WALL_STEEL(element))
693     return EL_WALL_STEEL_START;
694   else if (IS_WALL_WOOD(element))
695     return EL_WALL_WOOD_START;
696   else if (IS_WALL_ICE(element))
697     return EL_WALL_ICE_START;
698   else if (IS_WALL_AMOEBA(element))
699     return EL_WALL_AMOEBA_START;
700   else if (IS_DF_WALL_STEEL(element))
701     return EL_DF_WALL_STEEL_START;
702   else if (IS_DF_WALL_WOOD(element))
703     return EL_DF_WALL_WOOD_START;
704   else if (IS_CHAR(element))
705     return EL_CHAR_START;
706   else
707     return element;
708 }
709
710 int get_element_phase(int element)
711 {
712   return element - get_base_element(element);
713 }
714
715 int get_num_elements(int element)
716 {
717   if (IS_MIRROR(element) ||
718       IS_POLAR(element) ||
719       IS_BEAMER(element) ||
720       IS_DF_MIRROR(element) ||
721       IS_DF_MIRROR_AUTO(element))
722     return 16;
723   else if (IS_GRID_STEEL_FIXED(element) ||
724            IS_GRID_WOOD_FIXED(element) ||
725            IS_GRID_STEEL_AUTO(element) ||
726            IS_GRID_WOOD_AUTO(element))
727     return 8;
728   else if (IS_MIRROR_FIXED(element) ||
729            IS_POLAR_CROSS(element) ||
730            IS_MCDUFFIN(element) ||
731            IS_LASER(element) ||
732            IS_RECEIVER(element) ||
733            IS_PACMAN(element) ||
734            IS_GRID_STEEL(element) ||
735            IS_GRID_WOOD(element))
736     return 4;
737   else
738     return 1;
739 }
740
741 int get_rotated_element(int element, int step)
742 {
743   int base_element = get_base_element(element);
744   int num_elements = get_num_elements(element);
745   int element_phase = element - base_element;
746
747   return base_element + (element_phase + step + num_elements) % num_elements;
748 }
749
750 static int map_element(int element)
751 {
752   switch (element)
753   {
754     case EL_WALL_STEEL:         return EL_STEEL_WALL;
755     case EL_WALL_WOOD:          return EL_WOODEN_WALL;
756     case EL_WALL_ICE:           return EL_ICE_WALL;
757     case EL_WALL_AMOEBA:        return EL_AMOEBA_WALL;
758     case EL_DF_WALL_STEEL:      return EL_DF_STEEL_WALL;
759     case EL_DF_WALL_WOOD:       return EL_DF_WOODEN_WALL;
760
761     default:                    return element;
762   }
763 }
764
765 int el2gfx(int element)
766 {
767   element = map_element(element);
768
769   switch (element)
770   {
771     case EL_LIGHTBALL:
772       return IMG_MM_LIGHTBALL_RED + RND(3);
773
774     default:
775       return el2img_mm(element);
776   }
777 }
778
779 void RedrawPlayfield_MM(void)
780 {
781   DrawLevel_MM();
782   DrawLaser_MM();
783 }
784
785 void BlitScreenToBitmap_MM(Bitmap *target_bitmap)
786 {
787   BlitBitmap(drawto_field, target_bitmap,
788              REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
789 }