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