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