fixed mapping level file game elements between RND and MM formats (again)
[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       getGraphicSource(graphic, frame, &bitmap, &src_x, &src_y);
512
513       BlitBitmap(bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
514                  dst_x, dst_y);
515     }
516   }
517
518   MarkTileDirty(x, y);
519 }
520
521 void DrawElement_MM(int x, int y, int element)
522 {
523   if (element == EL_EMPTY)
524     DrawGraphic_MM(x, y, IMG_EMPTY);
525   else if (IS_WALL(element))
526     DrawWalls_MM(x, y, element);
527 #if 0
528   else if (IS_WALL_CHANGING(element) && IS_WALL_CHANGING(Feld[x][y]))
529   {
530     int wall_element = Feld[x][y] - EL_WALL_CHANGING + Store[x][y];
531
532     DrawWalls_MM(x, y, wall_element);
533   }
534 #endif
535   else if (element == EL_PACMAN)
536     DrawLevelField_MM(x, y);
537   else
538     DrawGraphic_MM(x, y, el2gfx(element));
539 }
540
541 void DrawMicroWalls_MM(int x, int y, int element)
542 {
543   Bitmap *bitmap;
544   int graphic = el2gfx(WALL_BASE(element));
545   int gx, gy;
546   int i;
547
548   getMicroGraphicSource(graphic, &bitmap, &gx, &gy);
549
550   for (i=0; i<4; i++)
551   {
552     int xpos = MICROLEV_XPOS + x * MICRO_TILEX + MICRO_WALLX * (i % 2);
553     int ypos = MICROLEV_YPOS + y * MICRO_TILEY + MICRO_WALLY * (i / 2);
554
555     if (element & (1 << i))
556       BlitBitmap(bitmap, drawto, gx, gy, MICRO_WALLX, MICRO_WALLY, xpos, ypos);
557     else
558       ClearRectangle(drawto, xpos, ypos, MICRO_WALLX, MICRO_WALLY);
559   }
560 }
561
562 void DrawMicroElement_MM(int x, int y, int element)
563 {
564   Bitmap *bitmap;
565   int graphic = el2gfx(element);
566   int gx, gy;
567
568   if (element == EL_EMPTY)
569     return;
570
571   if (IS_WALL(element))
572   {
573     DrawMicroWalls_MM(x, y, element);
574     return;
575   }
576
577   getMicroGraphicSource(graphic, &bitmap, &gx, &gy);
578
579   BlitBitmap(bitmap, drawto, gx, gy, MICRO_TILEX, MICRO_TILEY,
580              MICROLEV_XPOS + x * MICRO_TILEX, MICROLEV_YPOS + y * MICRO_TILEY);
581 }
582
583 void DrawMicroLevelExt_MM(int xpos, int ypos)
584 {
585   int x,y;
586
587   ClearRectangle(drawto, xpos, ypos, MICROLEV_XSIZE, MICROLEV_YSIZE);
588
589   for (x=0; x<STD_LEV_FIELDX; x++)
590     for (y=0; y<STD_LEV_FIELDY; y++)
591       DrawMicroElement_MM(x, y, Ur[x][y]);
592
593   redraw_mask |= REDRAW_FIELD;
594 }
595
596 void DrawMiniLevel_MM(int size_x, int size_y, int scroll_x, int scroll_y)
597 {
598   int x,y;
599
600   for(x=0; x<size_x; x++)
601     for(y=0; y<size_y; y++)
602       DrawMiniElementOrWall_MM(x, y, scroll_x, scroll_y);
603
604   redraw_mask |= REDRAW_FIELD;
605 }
606
607 int REQ_in_range(int x, int y)
608 {
609   if (y > DY+249 && y < DY+278)
610   {
611     if (x > DX+1 && x < DX+48)
612       return 1;
613     else if (x > DX+51 && x < DX+98)
614       return 2;
615   }
616   return 0;
617 }
618
619 Pixel ReadPixel(DrawBuffer *bitmap, int x, int y)
620 {
621   return GetPixel(bitmap, x, y);
622 }
623
624 void SetRGB(unsigned int pixel,
625             unsigned short red, unsigned short green, unsigned short blue)
626 {
627 }
628
629 int get_base_element(int element)
630 {
631   if (IS_MIRROR(element))
632     return EL_MIRROR_START;
633   else if (IS_MIRROR_FIXED(element))
634     return EL_MIRROR_FIXED_START;
635   else if (IS_POLAR(element))
636     return EL_POLAR_START;
637   else if (IS_POLAR_CROSS(element))
638     return EL_POLAR_CROSS_START;
639   else if (IS_BEAMER(element))
640     return EL_BEAMER_RED_START + BEAMER_NR(element) * 16;
641   else if (IS_FIBRE_OPTIC(element))
642     return EL_FIBRE_OPTIC_START + FIBRE_OPTIC_NR(element) * 2;
643   else if (IS_MCDUFFIN(element))
644     return EL_MCDUFFIN_START;
645   else if (IS_LASER(element))
646     return EL_LASER_START;
647   else if (IS_RECEIVER(element))
648     return EL_RECEIVER_START;
649   else if (IS_DF_MIRROR(element))
650     return EL_DF_MIRROR_START;
651   else if (IS_DF_MIRROR_AUTO(element))
652     return EL_DF_MIRROR_AUTO_START;
653   else if (IS_PACMAN(element))
654     return EL_PACMAN_START;
655   else if (IS_GRID_STEEL(element))
656     return EL_GRID_STEEL_START;
657   else if (IS_GRID_WOOD(element))
658     return EL_GRID_WOOD_START;
659   else if (IS_GRID_STEEL_FIXED(element))
660     return EL_GRID_STEEL_FIXED_START;
661   else if (IS_GRID_WOOD_FIXED(element))
662     return EL_GRID_WOOD_FIXED_START;
663   else if (IS_GRID_STEEL_AUTO(element))
664     return EL_GRID_STEEL_AUTO_START;
665   else if (IS_GRID_WOOD_AUTO(element))
666     return EL_GRID_WOOD_AUTO_START;
667   else if (IS_WALL_STEEL(element))
668     return EL_WALL_STEEL_START;
669   else if (IS_WALL_WOOD(element))
670     return EL_WALL_WOOD_START;
671   else if (IS_WALL_ICE(element))
672     return EL_WALL_ICE_START;
673   else if (IS_WALL_AMOEBA(element))
674     return EL_WALL_AMOEBA_START;
675   else if (IS_DF_WALL_STEEL(element))
676     return EL_DF_WALL_STEEL_START;
677   else if (IS_DF_WALL_WOOD(element))
678     return EL_DF_WALL_WOOD_START;
679   else if (IS_CHAR(element))
680     return EL_CHAR_START;
681   else
682     return element;
683 }
684
685 int get_element_phase(int element)
686 {
687   return element - get_base_element(element);
688 }
689
690 int get_num_elements(int element)
691 {
692   if (IS_MIRROR(element) ||
693       IS_POLAR(element) ||
694       IS_BEAMER(element) ||
695       IS_DF_MIRROR(element) ||
696       IS_DF_MIRROR_AUTO(element))
697     return 16;
698   else if (IS_GRID_STEEL_FIXED(element) ||
699            IS_GRID_WOOD_FIXED(element) ||
700            IS_GRID_STEEL_AUTO(element) ||
701            IS_GRID_WOOD_AUTO(element))
702     return 8;
703   else if (IS_MIRROR_FIXED(element) ||
704            IS_POLAR_CROSS(element) ||
705            IS_MCDUFFIN(element) ||
706            IS_LASER(element) ||
707            IS_RECEIVER(element) ||
708            IS_PACMAN(element) ||
709            IS_GRID_STEEL(element) ||
710            IS_GRID_WOOD(element))
711     return 4;
712   else
713     return 1;
714 }
715
716 int get_rotated_element(int element, int step)
717 {
718   int base_element = get_base_element(element);
719   int num_elements = get_num_elements(element);
720   int element_phase = element - base_element;
721
722   return base_element + (element_phase + step + num_elements) % num_elements;
723 }
724
725 static int map_element(int element)
726 {
727   switch (element)
728   {
729     case EL_WALL_STEEL:         return EL_STEEL_WALL;
730     case EL_WALL_WOOD:          return EL_WOODEN_WALL;
731     case EL_WALL_ICE:           return EL_ICE_WALL;
732     case EL_WALL_AMOEBA:        return EL_AMOEBA_WALL;
733     case EL_DF_WALL_STEEL:      return EL_DF_STEEL_WALL;
734     case EL_DF_WALL_WOOD:       return EL_DF_WOODEN_WALL;
735
736     default:                    return element;
737   }
738 }
739
740 int el2gfx(int element)
741 {
742   element = map_element(element);
743
744   switch (element)
745   {
746     case EL_LIGHTBALL:
747       return IMG_MM_LIGHTBALL_RED + RND(3);
748
749     default:
750       return el2img_mm(element);
751   }
752 }
753
754 void RedrawPlayfield_MM()
755 {
756   DrawLevel_MM();
757 }
758
759 void BlitScreenToBitmap_MM(Bitmap *target_bitmap)
760 {
761   BlitBitmap(drawto_field, target_bitmap,
762              REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
763 }