fixed wall animations for Mirror Magic game 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 /* forward declaration for internal use */
21 static int getGraphicAnimationPhase_MM(int, int, int);
22
23 void ClearWindow()
24 {
25   ClearRectangle(backbuffer, REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
26
27   SetDrawtoField(DRAW_BACKBUFFER);
28
29   redraw_mask |= REDRAW_FIELD;
30 }
31
32 static int getGraphicAnimationPhase_MM(int frames, int delay, int mode)
33 {
34   int phase;
35
36   if (mode == ANIM_PINGPONG)
37   {
38     int max_anim_frames = 2 * frames - 2;
39     phase = (FrameCounter % (delay * max_anim_frames)) / delay;
40     phase = (phase < frames ? phase : max_anim_frames - phase);
41   }
42   else
43     phase = (FrameCounter % (delay * frames)) / delay;
44
45   if (mode == ANIM_REVERSE)
46     phase = -phase;
47
48   return(phase);
49 }
50
51 void DrawGraphicAnimationExt_MM(int x, int y, int graphic,
52                                  int frames, int delay, int mode, int mask_mode)
53 {
54   int phase = getGraphicAnimationPhase_MM(frames, delay, mode);
55
56   if (!(FrameCounter % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
57   {
58     if (mask_mode == USE_MASKING)
59       DrawGraphicThruMask_MM(SCREENX(x), SCREENY(y), graphic + phase);
60     else
61       DrawGraphic_MM(SCREENX(x), SCREENY(y), graphic + phase);
62   }
63 }
64
65 void DrawGraphicAnimation_MM(int x, int y, int graphic,
66                           int frames, int delay, int mode)
67 {
68   DrawGraphicAnimationExt_MM(x, y, graphic, frames, delay, mode, NO_MASKING);
69 }
70
71 void DrawGraphicAnimationThruMask_MM(int x, int y, int graphic,
72                                   int frames, int delay, int mode)
73 {
74   DrawGraphicAnimationExt_MM(x, y, graphic, frames, delay, mode, USE_MASKING);
75 }
76
77 void DrawGraphic_MM(int x, int y, int graphic)
78 {
79 #if DEBUG
80   if (!IN_SCR_FIELD(x,y))
81   {
82     printf("DrawGraphic_MM(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
83     printf("DrawGraphic_MM(): This should never happen!\n");
84     return;
85   }
86 #endif
87
88   DrawGraphicExt_MM(drawto_field, FX + x*TILEX, FY + y*TILEY, graphic);
89   MarkTileDirty(x, y);
90 }
91
92 void DrawGraphicExt_MM(DrawBuffer *d, int x, int y, int graphic)
93 {
94   Bitmap *bitmap;
95   int src_x, src_y;
96
97   getGraphicSource(graphic, 0, &bitmap, &src_x, &src_y);
98   BlitBitmap(bitmap, d, src_x, src_y, TILEX, TILEY, x, y);
99 }
100
101 void DrawGraphicThruMask_MM(int x, int y, int graphic)
102 {
103 #if DEBUG
104   if (!IN_SCR_FIELD(x,y))
105   {
106     printf("DrawGraphicThruMask_MM(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
107     printf("DrawGraphicThruMask_MM(): This should never happen!\n");
108     return;
109   }
110 #endif
111
112   DrawGraphicThruMaskExt_MM(drawto_field, FX + x*TILEX, FY + y*TILEY, graphic);
113   MarkTileDirty(x,y);
114 }
115
116 void DrawGraphicThruMaskExt_MM(DrawBuffer *d, int dest_x, int dest_y,
117                                int graphic)
118 {
119   int src_x, src_y;
120   Bitmap *src_bitmap;
121
122   if (graphic == IMG_EMPTY)
123     return;
124
125   getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
126
127   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY, dest_x, dest_y);
128 }
129
130 void DrawMiniGraphic_MM(int x, int y, int graphic)
131 {
132   DrawMiniGraphicExt_MM(drawto, SX + x*MINI_TILEX, SY + y*MINI_TILEY, graphic);
133   MarkTileDirty(x/2, y/2);
134 }
135
136 void getMicroGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
137 {
138   getSizedGraphicSource(graphic, 0, TILESIZE / 4, bitmap, x, y);
139 }
140
141 void DrawMiniGraphicExt_MM(DrawBuffer *d, int x, int y, int graphic)
142 {
143   Bitmap *bitmap;
144   int src_x, src_y;
145
146   getMiniGraphicSource(graphic, &bitmap, &src_x, &src_y);
147   BlitBitmap(bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
148 }
149
150 void DrawGraphicShifted_MM(int x,int y, int dx,int dy, int graphic,
151                         int cut_mode, int mask_mode)
152 {
153   int width = TILEX, height = TILEY;
154   int cx = 0, cy = 0;
155   int src_x, src_y, dest_x, dest_y;
156   Bitmap *src_bitmap;
157
158   if (graphic < 0)
159   {
160     DrawGraphic_MM(x, y, graphic);
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       height -= dy;
224     else if (dy)                /* allg. Bewegung in y-Richtung */
225       MarkTileDirty(x, y + SIGN(dy));
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 = FX + x * TILEX + dx;
234   dest_y = FY + 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   {
247     BlitBitmapMasked(src_bitmap, drawto_field,
248                      src_x, src_y, TILEX, TILEY, dest_x, dest_y);
249   }
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 += 4 * !phase2;
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     sx = SCREENX(oldx);
364     sy = SCREENY(oldy);
365     horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
366                   MovDir[oldx][oldy] == MV_RIGHT);
367
368     DrawScreenElement_MM(x, y, EL_EMPTY);
369     element = Feld[oldx][oldy];
370
371     if (horiz_move)
372       DrawScreenElementShifted_MM(sx,sy, MovPos[oldx][oldy],0,element,NO_CUTTING);
373     else
374       DrawScreenElementShifted_MM(sx,sy, 0,MovPos[oldx][oldy],element,NO_CUTTING);
375   }
376   else if (IS_DRAWABLE(element))
377     DrawScreenElement_MM(x, y, element);
378   else
379     DrawScreenElement_MM(x, y, EL_EMPTY);
380 }
381
382 void DrawLevelField_MM(int x, int y)
383 {
384   DrawScreenField_MM(x, y);
385 }
386
387 void DrawMiniElement_MM(int x, int y, int element)
388 {
389   int graphic;
390
391   if (!element)
392   {
393     DrawMiniGraphic_MM(x, y, IMG_EMPTY);
394     return;
395   }
396
397   graphic = el2gfx(element);
398   DrawMiniGraphic_MM(x, y, graphic);
399 }
400
401 void DrawMiniElementOrWall_MM(int sx, int sy, int scroll_x, int scroll_y)
402 {
403   int x = sx + scroll_x, y = sy + scroll_y;
404
405   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
406     DrawMiniElement_MM(sx, sy, EL_EMPTY);
407   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
408     DrawMiniElement_MM(sx, sy, Feld[x][y]);
409 }
410
411 void DrawField_MM(int x, int y)
412 {
413   int element = Feld[x][y];
414
415   DrawElement_MM(x, y, element);
416 }
417
418 void DrawLevel_MM()
419 {
420   int x,y;
421
422   ClearWindow();
423
424   for (x=0; x<lev_fieldx; x++)
425     for (y=0; y<lev_fieldy; y++)
426       DrawField_MM(x, y);
427
428   redraw_mask |= REDRAW_FIELD;
429 }
430
431 void DrawWallsExt_MM(int x, int y, int element, int draw_mask)
432 {
433   Bitmap *bitmap;
434   int graphic = el2gfx(WALL_BASE(element));
435   int gx, gy;
436   int i;
437
438   getMiniGraphicSource(graphic, &bitmap, &gx, &gy);
439
440   if (game_status != LEVELED || !editor.draw_walls_masked)
441     DrawGraphic_MM(x, y, IMG_EMPTY);
442
443   /*
444   if (IS_WALL_WOOD(element) || IS_WALL_AMOEBA(element) ||
445       IS_DF_WALL_WOOD(element))
446     gx += MINI_TILEX;
447   if (IS_WALL_ICE(element) || IS_WALL_AMOEBA(element))
448     gy += MINI_TILEY;
449   */
450
451   for(i=0; i<4; i++)
452   {
453     int dest_x = SX + x * TILEX + MINI_TILEX * (i % 2);
454     int dest_y = SY + y * TILEY + MINI_TILEY * (i / 2);
455
456     if (!((1 << i) & draw_mask))
457       continue;
458
459     if (element & (1 << i))
460       BlitBitmap(bitmap, drawto, gx, gy, MINI_TILEX, MINI_TILEY,
461                  dest_x, dest_y);
462     else if (!editor.draw_walls_masked)
463       ClearRectangle(drawto, dest_x, dest_y, MINI_TILEX, MINI_TILEY);
464   }
465
466   MarkTileDirty(x, y);
467 }
468
469 void DrawWalls_MM(int x, int y, int element)
470 {
471   DrawWallsExt_MM(x, y, element, HIT_MASK_ALL);
472 }
473
474 void DrawWallsAnimation_MM(int x, int y, int element, int phase, int bit_mask)
475 {
476   int i;
477
478   if (phase == 0)
479   {
480     DrawWalls_MM(x, y, element);
481
482     return;
483   }
484
485   for (i = 0; i < 4; i++)
486   {
487     if (element & (1 << i))
488     {
489       int graphic;
490       int frame;
491       Bitmap *bitmap;
492       int src_x, src_y;
493       int dst_x = SX + x * TILEX + (i % 2) * MINI_TILEX;
494       int dst_y = SY + y * TILEY + (i / 2) * MINI_TILEY;
495
496       if (bit_mask & (1 << i))
497       {
498         graphic = (IS_WALL_AMOEBA(element) ?
499                    IMG_MM_AMOEBA_WALL_GROWING :
500                    IMG_MM_ICE_WALL_SHRINKING);
501         frame = phase;
502       }
503       else
504       {
505         graphic = (IS_WALL_AMOEBA(element) ?
506                    IMG_MM_AMOEBA_WALL :
507                    IMG_MM_ICE_WALL);
508         frame = 0;
509       }
510
511       getSizedGraphicSource(graphic, frame, MINI_TILESIZE, &bitmap,
512                             &src_x, &src_y);
513
514       BlitBitmap(bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
515                  dst_x, dst_y);
516     }
517   }
518
519   MarkTileDirty(x, y);
520 }
521
522 void DrawElement_MM(int x, int y, int element)
523 {
524   if (element == EL_EMPTY)
525     DrawGraphic_MM(x, y, IMG_EMPTY);
526   else if (IS_WALL(element))
527     DrawWalls_MM(x, y, element);
528 #if 0
529   else if (IS_WALL_CHANGING(element) && IS_WALL_CHANGING(Feld[x][y]))
530   {
531     int wall_element = Feld[x][y] - EL_WALL_CHANGING + Store[x][y];
532
533     DrawWalls_MM(x, y, wall_element);
534   }
535 #endif
536   else if (element == EL_PACMAN)
537     DrawLevelField_MM(x, y);
538   else
539     DrawGraphic_MM(x, y, el2gfx(element));
540 }
541
542 void DrawMicroWalls_MM(int x, int y, int element)
543 {
544   Bitmap *bitmap;
545   int graphic = el2gfx(WALL_BASE(element));
546   int gx, gy;
547   int i;
548
549   getMicroGraphicSource(graphic, &bitmap, &gx, &gy);
550
551   for (i=0; i<4; i++)
552   {
553     int xpos = MICROLEV_XPOS + x * MICRO_TILEX + MICRO_WALLX * (i % 2);
554     int ypos = MICROLEV_YPOS + y * MICRO_TILEY + MICRO_WALLY * (i / 2);
555
556     if (element & (1 << i))
557       BlitBitmap(bitmap, drawto, gx, gy, MICRO_WALLX, MICRO_WALLY, xpos, ypos);
558     else
559       ClearRectangle(drawto, xpos, ypos, MICRO_WALLX, MICRO_WALLY);
560   }
561 }
562
563 void DrawMicroElement_MM(int x, int y, int element)
564 {
565   Bitmap *bitmap;
566   int graphic = el2gfx(element);
567   int gx, gy;
568
569   if (element == EL_EMPTY)
570     return;
571
572   if (IS_WALL(element))
573   {
574     DrawMicroWalls_MM(x, y, element);
575     return;
576   }
577
578   getMicroGraphicSource(graphic, &bitmap, &gx, &gy);
579
580   BlitBitmap(bitmap, drawto, gx, gy, MICRO_TILEX, MICRO_TILEY,
581              MICROLEV_XPOS + x * MICRO_TILEX, MICROLEV_YPOS + y * MICRO_TILEY);
582 }
583
584 void DrawMicroLevelExt_MM(int xpos, int ypos)
585 {
586   int x,y;
587
588   ClearRectangle(drawto, xpos, ypos, MICROLEV_XSIZE, MICROLEV_YSIZE);
589
590   for (x=0; x<STD_LEV_FIELDX; x++)
591     for (y=0; y<STD_LEV_FIELDY; y++)
592       DrawMicroElement_MM(x, y, Ur[x][y]);
593
594   redraw_mask |= REDRAW_FIELD;
595 }
596
597 void DrawMiniLevel_MM(int size_x, int size_y, int scroll_x, int scroll_y)
598 {
599   int x,y;
600
601   for(x=0; x<size_x; x++)
602     for(y=0; y<size_y; y++)
603       DrawMiniElementOrWall_MM(x, y, scroll_x, scroll_y);
604
605   redraw_mask |= REDRAW_FIELD;
606 }
607
608 int REQ_in_range(int x, int y)
609 {
610   if (y > DY+249 && y < DY+278)
611   {
612     if (x > DX+1 && x < DX+48)
613       return 1;
614     else if (x > DX+51 && x < DX+98)
615       return 2;
616   }
617   return 0;
618 }
619
620 Pixel ReadPixel(DrawBuffer *bitmap, int x, int y)
621 {
622   return GetPixel(bitmap, x, y);
623 }
624
625 void SetRGB(unsigned int pixel,
626             unsigned short red, unsigned short green, unsigned short blue)
627 {
628 }
629
630 int get_base_element(int element)
631 {
632   if (IS_MIRROR(element))
633     return EL_MIRROR_START;
634   else if (IS_MIRROR_FIXED(element))
635     return EL_MIRROR_FIXED_START;
636   else if (IS_POLAR(element))
637     return EL_POLAR_START;
638   else if (IS_POLAR_CROSS(element))
639     return EL_POLAR_CROSS_START;
640   else if (IS_BEAMER(element))
641     return EL_BEAMER_RED_START + BEAMER_NR(element) * 16;
642   else if (IS_FIBRE_OPTIC(element))
643     return EL_FIBRE_OPTIC_START + FIBRE_OPTIC_NR(element) * 2;
644   else if (IS_MCDUFFIN(element))
645     return EL_MCDUFFIN_START;
646   else if (IS_LASER(element))
647     return EL_LASER_START;
648   else if (IS_RECEIVER(element))
649     return EL_RECEIVER_START;
650   else if (IS_DF_MIRROR(element))
651     return EL_DF_MIRROR_START;
652   else if (IS_DF_MIRROR_AUTO(element))
653     return EL_DF_MIRROR_AUTO_START;
654   else if (IS_PACMAN(element))
655     return EL_PACMAN_START;
656   else if (IS_GRID_STEEL(element))
657     return EL_GRID_STEEL_START;
658   else if (IS_GRID_WOOD(element))
659     return EL_GRID_WOOD_START;
660   else if (IS_GRID_STEEL_FIXED(element))
661     return EL_GRID_STEEL_FIXED_START;
662   else if (IS_GRID_WOOD_FIXED(element))
663     return EL_GRID_WOOD_FIXED_START;
664   else if (IS_GRID_STEEL_AUTO(element))
665     return EL_GRID_STEEL_AUTO_START;
666   else if (IS_GRID_WOOD_AUTO(element))
667     return EL_GRID_WOOD_AUTO_START;
668   else if (IS_WALL_STEEL(element))
669     return EL_WALL_STEEL_START;
670   else if (IS_WALL_WOOD(element))
671     return EL_WALL_WOOD_START;
672   else if (IS_WALL_ICE(element))
673     return EL_WALL_ICE_START;
674   else if (IS_WALL_AMOEBA(element))
675     return EL_WALL_AMOEBA_START;
676   else if (IS_DF_WALL_STEEL(element))
677     return EL_DF_WALL_STEEL_START;
678   else if (IS_DF_WALL_WOOD(element))
679     return EL_DF_WALL_WOOD_START;
680   else if (IS_CHAR(element))
681     return EL_CHAR_START;
682   else
683     return element;
684 }
685
686 int get_element_phase(int element)
687 {
688   return element - get_base_element(element);
689 }
690
691 int get_num_elements(int element)
692 {
693   if (IS_MIRROR(element) ||
694       IS_POLAR(element) ||
695       IS_BEAMER(element) ||
696       IS_DF_MIRROR(element) ||
697       IS_DF_MIRROR_AUTO(element))
698     return 16;
699   else if (IS_GRID_STEEL_FIXED(element) ||
700            IS_GRID_WOOD_FIXED(element) ||
701            IS_GRID_STEEL_AUTO(element) ||
702            IS_GRID_WOOD_AUTO(element))
703     return 8;
704   else if (IS_MIRROR_FIXED(element) ||
705            IS_POLAR_CROSS(element) ||
706            IS_MCDUFFIN(element) ||
707            IS_LASER(element) ||
708            IS_RECEIVER(element) ||
709            IS_PACMAN(element) ||
710            IS_GRID_STEEL(element) ||
711            IS_GRID_WOOD(element))
712     return 4;
713   else
714     return 1;
715 }
716
717 int get_rotated_element(int element, int step)
718 {
719   int base_element = get_base_element(element);
720   int num_elements = get_num_elements(element);
721   int element_phase = element - base_element;
722
723   return base_element + (element_phase + step + num_elements) % num_elements;
724 }
725
726 static int map_element(int element)
727 {
728   switch (element)
729   {
730     case EL_WALL_STEEL:         return EL_STEEL_WALL;
731     case EL_WALL_WOOD:          return EL_WOODEN_WALL;
732     case EL_WALL_ICE:           return EL_ICE_WALL;
733     case EL_WALL_AMOEBA:        return EL_AMOEBA_WALL;
734     case EL_DF_WALL_STEEL:      return EL_DF_STEEL_WALL;
735     case EL_DF_WALL_WOOD:       return EL_DF_WOODEN_WALL;
736
737     default:                    return element;
738   }
739 }
740
741 int el2gfx(int element)
742 {
743   element = map_element(element);
744
745   switch (element)
746   {
747     case EL_LIGHTBALL:
748       return IMG_MM_LIGHTBALL_RED + RND(3);
749
750     default:
751       return el2img_mm(element);
752   }
753 }
754
755 void RedrawPlayfield_MM()
756 {
757   DrawLevel_MM();
758 }
759
760 void BlitScreenToBitmap_MM(Bitmap *target_bitmap)
761 {
762   BlitBitmap(drawto_field, target_bitmap,
763              REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
764 }