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