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