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