added and adjusted source files 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
85 #if 1
86     {
87       int i=0;
88       i=i/i;
89     }
90 #endif
91
92     return;
93   }
94 #endif
95
96   DrawGraphicExt_MM(drawto_field, FX + x*TILEX, FY + y*TILEY, graphic);
97   MarkTileDirty(x, y);
98 }
99
100 void DrawGraphicExt_MM(DrawBuffer *d, int x, int y, int graphic)
101 {
102   Bitmap *bitmap;
103   int src_x, src_y;
104
105   getGraphicSource(graphic, 0, &bitmap, &src_x, &src_y);
106   BlitBitmap(bitmap, d, src_x, src_y, TILEX, TILEY, x, y);
107 }
108
109 void DrawGraphicThruMask_MM(int x, int y, int graphic)
110 {
111 #if DEBUG
112   if (!IN_SCR_FIELD(x,y))
113   {
114     printf("DrawGraphicThruMask_MM(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
115     printf("DrawGraphicThruMask_MM(): This should never happen!\n");
116     return;
117   }
118 #endif
119
120   DrawGraphicThruMaskExt_MM(drawto_field, FX + x*TILEX, FY + y*TILEY, graphic);
121   MarkTileDirty(x,y);
122 }
123
124 void DrawGraphicThruMaskExt_MM(DrawBuffer *d, int dest_x, int dest_y, int graphic)
125 {
126   int src_x, src_y;
127   Bitmap *src_bitmap;
128
129   if (graphic == GFX_EMPTY)
130     return;
131
132   getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
133
134   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY, dest_x, dest_y);
135 }
136
137 void DrawMiniGraphic_MM(int x, int y, int graphic)
138 {
139   DrawMiniGraphicExt_MM(drawto, SX + x*MINI_TILEX, SY + y*MINI_TILEY, graphic);
140   MarkTileDirty(x/2, y/2);
141 }
142
143 void getMicroGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
144 {
145   getSizedGraphicSource(graphic, 0, TILESIZE / 4, bitmap, x, y);
146 }
147
148 void DrawMiniGraphicExt_MM(DrawBuffer *d, int x, int y, int graphic)
149 {
150   Bitmap *bitmap;
151   int src_x, src_y;
152
153   getMiniGraphicSource(graphic, &bitmap, &src_x, &src_y);
154   BlitBitmap(bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
155 }
156
157 void DrawGraphicShifted_MM(int x,int y, int dx,int dy, int graphic,
158                         int cut_mode, int mask_mode)
159 {
160   int width = TILEX, height = TILEY;
161   int cx = 0, cy = 0;
162   int src_x, src_y, dest_x, dest_y;
163   Bitmap *src_bitmap;
164
165   if (graphic < 0)
166   {
167     DrawGraphic_MM(x, y, graphic);
168     return;
169   }
170
171   if (dx || dy)                 /* Verschiebung der Grafik? */
172   {
173     if (x < BX1)                /* Element kommt von links ins Bild */
174     {
175       x = BX1;
176       width = dx;
177       cx = TILEX - dx;
178       dx = 0;
179     }
180     else if (x > BX2)           /* Element kommt von rechts ins Bild */
181     {
182       x = BX2;
183       width = -dx;
184       dx = TILEX + dx;
185     }
186     else if (x==BX1 && dx < 0)  /* Element verläßt links das Bild */
187     {
188       width += dx;
189       cx = -dx;
190       dx = 0;
191     }
192     else if (x==BX2 && dx > 0)  /* Element verläßt rechts das Bild */
193       width -= dx;
194     else if (dx)                /* allg. Bewegung in x-Richtung */
195       MarkTileDirty(x + SIGN(dx), y);
196
197     if (y < BY1)                /* Element kommt von oben ins Bild */
198     {
199       if (cut_mode==CUT_BELOW)  /* Element oberhalb des Bildes */
200         return;
201
202       y = BY1;
203       height = dy;
204       cy = TILEY - dy;
205       dy = 0;
206     }
207     else if (y > BY2)           /* Element kommt von unten ins Bild */
208     {
209       y = BY2;
210       height = -dy;
211       dy = TILEY + dy;
212     }
213     else if (y==BY1 && dy < 0)  /* Element verläßt oben das Bild */
214     {
215       height += dy;
216       cy = -dy;
217       dy = 0;
218     }
219     else if (dy > 0 && cut_mode == CUT_ABOVE)
220     {
221       if (y == BY2)             /* Element unterhalb des Bildes */
222         return;
223
224       height = dy;
225       cy = TILEY - dy;
226       dy = TILEY;
227       MarkTileDirty(x, y + 1);
228     }                           /* Element verläßt unten das Bild */
229     else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
230       height -= dy;
231     else if (dy)                /* allg. Bewegung in y-Richtung */
232       MarkTileDirty(x, y + SIGN(dy));
233   }
234
235   getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
236
237   src_x += cx;
238   src_y += cy;
239
240   dest_x = FX + x * TILEX + dx;
241   dest_y = FY + y * TILEY + dy;
242
243 #if DEBUG
244   if (!IN_SCR_FIELD(x,y))
245   {
246     printf("DrawGraphicShifted_MM(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
247     printf("DrawGraphicShifted_MM(): This should never happen!\n");
248     return;
249   }
250 #endif
251
252   if (mask_mode == USE_MASKING)
253   {
254     BlitBitmapMasked(src_bitmap, drawto_field,
255                      src_x, src_y, TILEX, TILEY, dest_x, dest_y);
256   }
257   else
258     BlitBitmap(src_bitmap, drawto_field,
259                src_x, src_y, width, height, dest_x, dest_y);
260
261   MarkTileDirty(x,y);
262 }
263
264 void DrawGraphicShiftedThruMask_MM(int x,int y, int dx,int dy, int graphic,
265                                 int cut_mode)
266 {
267   DrawGraphicShifted_MM(x,y, dx,dy, graphic, cut_mode, USE_MASKING);
268 }
269
270 void DrawScreenElementExt_MM(int x, int y, int dx, int dy, int element,
271                           int cut_mode, int mask_mode)
272 {
273   int ux = LEVELX(x), uy = LEVELY(y);
274   int graphic = el2gfx(element);
275   int phase8 = ABS(MovPos[ux][uy]) / (TILEX / 8);
276   int phase2  = phase8 / 4;
277   int dir = MovDir[ux][uy];
278
279   if (element == EL_PACMAN)
280   {
281     graphic += 4 * !phase2;
282
283     if (dir == MV_UP)
284       graphic += 1;
285     else if (dir == MV_LEFT)
286       graphic += 2;
287     else if (dir == MV_DOWN)
288       graphic += 3;
289   }
290
291   if (dx || dy)
292     DrawGraphicShifted_MM(x, y, dx, dy, graphic, cut_mode, mask_mode);
293   else if (mask_mode == USE_MASKING)
294     DrawGraphicThruMask_MM(x, y, graphic);
295   else
296     DrawGraphic_MM(x, y, graphic);
297 }
298
299 void DrawLevelElementExt_MM(int x, int y, int dx, int dy, int element,
300                          int cut_mode, int mask_mode)
301 {
302   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
303     DrawScreenElementExt_MM(SCREENX(x), SCREENY(y), dx, dy, element,
304                          cut_mode, mask_mode);
305 }
306
307 void DrawScreenElementShifted_MM(int x, int y, int dx, int dy, int element,
308                               int cut_mode)
309 {
310   DrawScreenElementExt_MM(x, y, dx, dy, element, cut_mode, NO_MASKING);
311 }
312
313 void DrawLevelElementShifted_MM(int x, int y, int dx, int dy, int element,
314                              int cut_mode)
315 {
316   DrawLevelElementExt_MM(x, y, dx, dy, element, cut_mode, NO_MASKING);
317 }
318
319 void DrawScreenElementThruMask_MM(int x, int y, int element)
320 {
321   DrawScreenElementExt_MM(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
322 }
323
324 void DrawLevelElementThruMask_MM(int x, int y, int element)
325 {
326   DrawLevelElementExt_MM(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
327 }
328
329 void DrawLevelFieldThruMask_MM(int x, int y)
330 {
331   DrawLevelElementExt_MM(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
332 }
333
334 void DrawScreenElement_MM(int x, int y, int element)
335 {
336   DrawScreenElementExt_MM(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
337 }
338
339 void DrawLevelElement_MM(int x, int y, int element)
340 {
341   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
342     DrawScreenElement_MM(SCREENX(x), SCREENY(y), element);
343 }
344
345 void DrawScreenField_MM(int x, int y)
346 {
347   int element = Feld[x][y];
348
349   if (!IN_LEV_FIELD(x, y))
350     return;
351
352   if (IS_MOVING(x, y))
353   {
354     int horiz_move = (MovDir[x][y] == MV_LEFT || MovDir[x][y] == MV_RIGHT);
355
356     DrawScreenElement_MM(x, y, EL_EMPTY);
357
358     if (horiz_move)
359       DrawScreenElementShifted_MM(x, y, MovPos[x][y], 0, element, NO_CUTTING);
360     else
361       DrawScreenElementShifted_MM(x, y, 0, MovPos[x][y], element, NO_CUTTING);
362   }
363   else if (IS_BLOCKED(x, y))
364   {
365     int oldx, oldy;
366     int sx, sy;
367     int horiz_move;
368
369     Blocked2Moving(x, y, &oldx, &oldy);
370     sx = SCREENX(oldx);
371     sy = SCREENY(oldy);
372     horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
373                   MovDir[oldx][oldy] == MV_RIGHT);
374
375     DrawScreenElement_MM(x, y, EL_EMPTY);
376     element = Feld[oldx][oldy];
377
378     if (horiz_move)
379       DrawScreenElementShifted_MM(sx,sy, MovPos[oldx][oldy],0,element,NO_CUTTING);
380     else
381       DrawScreenElementShifted_MM(sx,sy, 0,MovPos[oldx][oldy],element,NO_CUTTING);
382   }
383   else if (IS_DRAWABLE(element))
384     DrawScreenElement_MM(x, y, element);
385   else
386     DrawScreenElement_MM(x, y, EL_EMPTY);
387 }
388
389 void DrawLevelField_MM(int x, int y)
390 {
391   DrawScreenField_MM(x, y);
392 }
393
394 void DrawMiniElement_MM(int x, int y, int element)
395 {
396   int graphic;
397
398   if (!element)
399   {
400     DrawMiniGraphic_MM(x, y, GFX_EMPTY);
401     return;
402   }
403
404   graphic = el2gfx(element);
405   DrawMiniGraphic_MM(x, y, graphic);
406 }
407
408 void DrawMiniElementOrWall_MM(int sx, int sy, int scroll_x, int scroll_y)
409 {
410   int x = sx + scroll_x, y = sy + scroll_y;
411
412   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
413     DrawMiniElement_MM(sx, sy, EL_EMPTY);
414   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
415     DrawMiniElement_MM(sx, sy, Feld[x][y]);
416 }
417
418 void DrawField_MM(int x, int y)
419 {
420   int element = Feld[x][y];
421
422   DrawElement_MM(x, y, element);
423 }
424
425 void DrawLevel_MM()
426 {
427   int x,y;
428
429   ClearWindow();
430
431   for (x=0; x<lev_fieldx; x++)
432     for (y=0; y<lev_fieldy; y++)
433       DrawField_MM(x, y);
434
435   redraw_mask |= REDRAW_FIELD;
436 }
437
438 void DrawWallsExt_MM(int x, int y, int element, int draw_mask)
439 {
440   Bitmap *bitmap;
441   int graphic = el2gfx(WALL_BASE(element));
442   int gx, gy;
443   int i;
444
445   getMiniGraphicSource(graphic, &bitmap, &gx, &gy);
446
447   if (game_status != LEVELED || !editor.draw_walls_masked)
448     DrawGraphic_MM(x, y, GFX_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 = SX + x * TILEX + MINI_TILEX * (i % 2);
461     int dest_y = SY + 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 if (!editor.draw_walls_masked)
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 graphic = GFX_WALL_SEVERAL;
484   int graphic_anim = graphic + (phase + 1) / 2;
485   int dx = (IS_WALL_AMOEBA(element) ? MINI_TILEX : 0);
486   int dy = MINI_TILEY;
487   int dx_anim = dx;
488   int dy_anim = ((phase + 1) % 2) * MINI_TILEY;
489   int i;
490
491   Bitmap *bitmap, *bitmap_anim;
492   int src_x, src_y;
493   int src_x_anim, src_y_anim;
494
495   getGraphicSource(graphic, 0, &bitmap, &src_x, &src_y);
496   getGraphicSource(graphic_anim, 0, &bitmap_anim, &src_x_anim, &src_y_anim);
497
498   if (phase == 0)
499   {
500     DrawWalls_MM(x, y, element);
501     return;
502   }
503
504   for(i=0; i<4; i++)
505   {
506     if (element & (1 << i))
507     {
508       int dest_x = SX + x * TILEX + MINI_TILEX * (i % 2);
509       int dest_y = SY + y * TILEY + MINI_TILEY * (i / 2);
510       int gx, gy;
511
512       if (bit_mask & (1 << i))
513       {
514         gx = src_x_anim + dx_anim;
515         gy = src_y_anim + dy_anim;
516
517         BlitBitmap(bitmap_anim, drawto, gx, gy, MINI_TILEX, MINI_TILEY,
518                    dest_x, dest_y);
519       }
520       else
521       {
522         gx = src_x + dx;
523         gy = src_y + dy;
524
525         BlitBitmap(bitmap, drawto, gx, gy, MINI_TILEX, MINI_TILEY,
526                    dest_x, dest_y);
527       }
528     }
529   }
530
531   MarkTileDirty(x, y);
532 }
533
534 void DrawElement_MM(int x, int y, int element)
535 {
536   if (element == EL_EMPTY)
537     DrawGraphic_MM(x, y, GFX_EMPTY);
538   else if (IS_WALL(element))
539     DrawWalls_MM(x, y, element);
540 #if 0
541   else if (IS_WALL_CHANGING(element) && IS_WALL_CHANGING(Feld[x][y]))
542   {
543     int wall_element = Feld[x][y] - EL_WALL_CHANGING + Store[x][y];
544
545     DrawWalls_MM(x, y, wall_element);
546   }
547 #endif
548   else if (element == EL_PACMAN)
549     DrawLevelField_MM(x, y);
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     return;
588   }
589
590   getMicroGraphicSource(graphic, &bitmap, &gx, &gy);
591
592   BlitBitmap(bitmap, drawto, gx, gy, MICRO_TILEX, MICRO_TILEY,
593              MICROLEV_XPOS + x * MICRO_TILEX, MICROLEV_YPOS + y * MICRO_TILEY);
594 }
595
596 void DrawMicroLevelExt_MM(int xpos, int ypos)
597 {
598   int x,y;
599
600   ClearRectangle(drawto, xpos, ypos, MICROLEV_XSIZE, MICROLEV_YSIZE);
601
602   for (x=0; x<STD_LEV_FIELDX; x++)
603     for (y=0; y<STD_LEV_FIELDY; y++)
604       DrawMicroElement_MM(x, y, Ur[x][y]);
605
606   redraw_mask |= REDRAW_FIELD;
607 }
608
609 void DrawMiniLevel_MM(int size_x, int size_y, int scroll_x, int scroll_y)
610 {
611   int x,y;
612
613   for(x=0; x<size_x; x++)
614     for(y=0; y<size_y; y++)
615       DrawMiniElementOrWall_MM(x, y, scroll_x, scroll_y);
616
617   redraw_mask |= REDRAW_FIELD;
618 }
619
620 int REQ_in_range(int x, int y)
621 {
622   if (y > DY+249 && y < DY+278)
623   {
624     if (x > DX+1 && x < DX+48)
625       return 1;
626     else if (x > DX+51 && x < DX+98)
627       return 2;
628   }
629   return 0;
630 }
631
632 Pixel ReadPixel(DrawBuffer *bitmap, int x, int y)
633 {
634 #if defined(TARGET_SDL) || defined(TARGET_ALLEGRO)
635   return GetPixel(bitmap, x, y);
636 #else
637   /* GetPixel() does also work for X11, but we use some optimization here */
638   unsigned int pixel_value;
639
640   if (bitmap == pix[PIX_BACK])
641   {
642     /* when reading pixel values from images, it is much faster to use
643        client side images (XImage) than server side images (Pixmap) */
644     static XImage *client_image = NULL;
645
646     if (client_image == NULL)   /* init image cache, if not existing */
647       client_image = XGetImage(display, bitmap->drawable,
648                                0,0, WIN_XSIZE,WIN_YSIZE, AllPlanes, ZPixmap);
649
650     pixel_value = XGetPixel(client_image, x, y);
651   }
652   else
653   {
654     XImage *pixel_image;
655
656     pixel_image = XGetImage(display, bitmap->drawable, x, y, 1, 1,
657                             AllPlanes, ZPixmap);
658     pixel_value = XGetPixel(pixel_image, 0, 0);
659
660     XDestroyImage(pixel_image);
661   }
662
663   return pixel_value;
664 #endif
665 }
666
667 void SetRGB(unsigned int pixel,
668             unsigned short red, unsigned short green, unsigned short blue)
669 {
670   return;
671
672 #if 0
673   XColor color;
674
675   if (color_status==STATIC_COLORS)
676     return;
677
678   color.pixel = pixel;
679   color.red = red;
680   color.green = green;
681   color.blue = blue;
682   color.flags = DoRed | DoGreen | DoBlue;
683   XStoreColor(display, cmap, &color);
684   XFlush(display);
685 #endif
686 }
687
688 int get_base_element(int element)
689 {
690   if (IS_MIRROR(element))
691     return EL_MIRROR_START;
692   else if (IS_MIRROR_FIXED(element))
693     return EL_MIRROR_FIXED_START;
694   else if (IS_POLAR(element))
695     return EL_POLAR_START;
696   else if (IS_POLAR_CROSS(element))
697     return EL_POLAR_CROSS_START;
698   else if (IS_BEAMER(element))
699     return EL_BEAMER_RED_START + BEAMER_NR(element) * 16;
700   else if (IS_FIBRE_OPTIC(element))
701     return EL_FIBRE_OPTIC_START + FIBRE_OPTIC_NR(element) * 2;
702   else if (IS_MCDUFFIN(element))
703     return EL_MCDUFFIN_START;
704   else if (IS_LASER(element))
705     return EL_LASER_START;
706   else if (IS_RECEIVER(element))
707     return EL_RECEIVER_START;
708   else if (IS_DF_MIRROR(element))
709     return EL_DF_MIRROR_START;
710   else if (IS_DF_MIRROR_AUTO(element))
711     return EL_DF_MIRROR_AUTO_START;
712   else if (IS_PACMAN(element))
713     return EL_PACMAN_START;
714   else if (IS_GRID_STEEL(element))
715     return EL_GRID_STEEL_START;
716   else if (IS_GRID_WOOD(element))
717     return EL_GRID_WOOD_START;
718   else if (IS_GRID_STEEL_FIXED(element))
719     return EL_GRID_STEEL_FIXED_START;
720   else if (IS_GRID_WOOD_FIXED(element))
721     return EL_GRID_WOOD_FIXED_START;
722   else if (IS_GRID_STEEL_AUTO(element))
723     return EL_GRID_STEEL_AUTO_START;
724   else if (IS_GRID_WOOD_AUTO(element))
725     return EL_GRID_WOOD_AUTO_START;
726   else if (IS_WALL_STEEL(element))
727     return EL_WALL_STEEL_START;
728   else if (IS_WALL_WOOD(element))
729     return EL_WALL_WOOD_START;
730   else if (IS_WALL_ICE(element))
731     return EL_WALL_ICE_START;
732   else if (IS_WALL_AMOEBA(element))
733     return EL_WALL_AMOEBA_START;
734   else if (IS_DF_WALL_STEEL(element))
735     return EL_DF_WALL_STEEL_START;
736   else if (IS_DF_WALL_WOOD(element))
737     return EL_DF_WALL_WOOD_START;
738   else if (IS_CHAR(element))
739     return EL_CHAR_START;
740   else
741     return element;
742 }
743
744 int get_element_phase(int element)
745 {
746   return element - get_base_element(element);
747 }
748
749 int get_num_elements(int element)
750 {
751   if (IS_MIRROR(element) ||
752       IS_POLAR(element) ||
753       IS_BEAMER(element) ||
754       IS_DF_MIRROR(element) ||
755       IS_DF_MIRROR_AUTO(element))
756     return 16;
757   else if (IS_GRID_STEEL_FIXED(element) ||
758            IS_GRID_WOOD_FIXED(element) ||
759            IS_GRID_STEEL_AUTO(element) ||
760            IS_GRID_WOOD_AUTO(element))
761     return 8;
762   else if (IS_MIRROR_FIXED(element) ||
763            IS_POLAR_CROSS(element) ||
764            IS_MCDUFFIN(element) ||
765            IS_LASER(element) ||
766            IS_RECEIVER(element) ||
767            IS_PACMAN(element) ||
768            IS_GRID_STEEL(element) ||
769            IS_GRID_WOOD(element))
770     return 4;
771   else
772     return 1;
773 }
774
775 int get_rotated_element(int element, int step)
776 {
777   int base_element = get_base_element(element);
778   int num_elements = get_num_elements(element);
779   int element_phase = element - base_element;
780
781   return base_element + (element_phase + step + num_elements) % num_elements;
782 }
783
784 int el2gfx(int element)
785 {
786   switch(element)
787   {
788     case EL_EMPTY:              return -1;
789     case EL_GRID_STEEL_00:      return GFX_GRID_STEEL_00;
790     case EL_GRID_STEEL_01:      return GFX_GRID_STEEL_01;
791     case EL_GRID_STEEL_02:      return GFX_GRID_STEEL_02;
792     case EL_GRID_STEEL_03:      return GFX_GRID_STEEL_03;
793     case EL_MCDUFFIN_RIGHT:     return GFX_MCDUFFIN_RIGHT;
794     case EL_MCDUFFIN_UP:        return GFX_MCDUFFIN_UP;
795     case EL_MCDUFFIN_LEFT:      return GFX_MCDUFFIN_LEFT;
796     case EL_MCDUFFIN_DOWN:      return GFX_MCDUFFIN_DOWN;
797     case EL_EXIT_CLOSED:        return GFX_EXIT_CLOSED;
798     case EL_EXIT_OPENING_1:     return GFX_EXIT_OPENING_1;
799     case EL_EXIT_OPENING_2:     return GFX_EXIT_OPENING_2;
800     case EL_EXIT_OPEN:          return GFX_EXIT_OPEN;
801     case EL_KETTLE:             return GFX_KETTLE;
802     case EL_BOMB:               return GFX_BOMB;
803     case EL_PRISM:              return GFX_PRISM;
804     case EL_BLOCK_WOOD:         return GFX_BLOCK_WOOD;
805     case EL_BALL_GRAY:          return GFX_BALL_GRAY;
806     case EL_FUSE_ON:            return GFX_FUSE_ON;
807     case EL_PACMAN_RIGHT:       return GFX_PACMAN_RIGHT;
808     case EL_PACMAN_UP:          return GFX_PACMAN_UP;
809     case EL_PACMAN_LEFT:        return GFX_PACMAN_LEFT;
810     case EL_PACMAN_DOWN:        return GFX_PACMAN_DOWN;
811     case EL_POLAR_CROSS_00:     return GFX_POLAR_CROSS_00;
812     case EL_POLAR_CROSS_01:     return GFX_POLAR_CROSS_01;
813     case EL_POLAR_CROSS_02:     return GFX_POLAR_CROSS_02;
814     case EL_POLAR_CROSS_03:     return GFX_POLAR_CROSS_03;
815     case EL_MIRROR_FIXED_00:    return GFX_MIRROR_FIXED_00;
816     case EL_MIRROR_FIXED_01:    return GFX_MIRROR_FIXED_01;
817     case EL_MIRROR_FIXED_02:    return GFX_MIRROR_FIXED_02;
818     case EL_MIRROR_FIXED_03:    return GFX_MIRROR_FIXED_03;
819     case EL_GATE_STONE:         return GFX_GATE_STONE;
820     case EL_KEY:                return GFX_KEY;
821     case EL_LIGHTBULB_ON:       return GFX_LIGHTBULB_ON;
822     case EL_LIGHTBULB_OFF:      return GFX_LIGHTBULB_OFF;
823     case EL_LIGHTBALL:          return GFX_BALL_RED + RND(3);;
824     case EL_BLOCK_STONE:        return GFX_BLOCK_STONE;
825     case EL_GATE_WOOD:          return GFX_GATE_WOOD;
826     case EL_FUEL_FULL:          return GFX_FUEL_FULL;
827     case EL_GRID_WOOD_00:       return GFX_GRID_WOOD_00;
828     case EL_GRID_WOOD_01:       return GFX_GRID_WOOD_01;
829     case EL_GRID_WOOD_02:       return GFX_GRID_WOOD_02;
830     case EL_GRID_WOOD_03:       return GFX_GRID_WOOD_03;
831     case EL_FUEL_EMPTY:         return GFX_FUEL_EMPTY;
832     case EL_FUSE_OFF:           return GFX_FUSE_OFF;
833     case EL_PACMAN:             return GFX_PACMAN;
834     case EL_REFRACTOR:          return GFX_REFRACTOR;
835     case EL_CELL:               return GFX_CELL;
836     case EL_MINE:               return GFX_MINE;
837
838     /* pseudo-graphics; will be mapped to other graphics */
839     case EL_WALL_STEEL:         return GFX_WALL_STEEL;
840     case EL_WALL_WOOD:          return GFX_WALL_WOOD;
841     case EL_WALL_ICE:           return GFX_WALL_ICE;
842     case EL_WALL_AMOEBA:        return GFX_WALL_AMOEBA;
843     case EL_DF_WALL_STEEL:      return GFX_DF_WALL_STEEL;
844     case EL_DF_WALL_WOOD:       return GFX_DF_WALL_WOOD;
845
846     default:
847     {
848       boolean ed = (game_status == LEVELED);
849       int base_element = get_base_element(element);
850       int element_phase = element - base_element;
851       int base_graphic;
852
853       if (IS_BEAMER(element))
854         element_phase = element - EL_BEAMER_RED_START;
855       else if (IS_FIBRE_OPTIC(element))
856         element_phase = element - EL_FIBRE_OPTIC_START;
857
858       if (IS_MIRROR(element))
859         base_graphic = GFX_MIRROR_START;
860       else if (IS_BEAMER_OLD(element))
861         base_graphic = GFX_BEAMER_START;
862       else if (IS_POLAR(element))
863         base_graphic = GFX_POLAR_START;
864       else if (IS_CHAR(element))
865         base_graphic = GFX_CHAR_START;
866       else if (IS_GRID_WOOD_FIXED(element))
867         base_graphic = GFX_GRID_WOOD_FIXED_00;
868       else if (IS_GRID_STEEL_FIXED(element))
869         base_graphic = GFX_GRID_STEEL_FIXED_00;
870       else if (IS_DF_MIRROR(element))
871         base_graphic = GFX_DF_MIRROR_00;
872       else if (IS_LASER(element))
873         base_graphic = GFX_LASER_RIGHT;
874       else if (IS_RECEIVER(element))
875         base_graphic = GFX_RECEIVER_RIGHT;
876       else if (IS_DF_MIRROR(element))
877         base_graphic = GFX_DF_MIRROR_00;
878       else if (IS_FIBRE_OPTIC(element))
879         base_graphic = (ed ? GFX_FIBRE_OPTIC_ED_00 : GFX_FIBRE_OPTIC_00);
880       else if (IS_GRID_WOOD_AUTO(element))
881         base_graphic = (ed ? GFX_GRID_WOOD_AUTO_00 : GFX_GRID_WOOD_FIXED_00);
882       else if (IS_GRID_STEEL_AUTO(element))
883         base_graphic = (ed ? GFX_GRID_STEEL_AUTO_00 : GFX_GRID_STEEL_FIXED_00);
884       else if (IS_DF_MIRROR_AUTO(element))
885         base_graphic = (ed ? GFX_DF_MIRROR_AUTO_00 : GFX_DF_MIRROR_00);
886       else if (IS_BEAMER(element))
887         base_graphic = GFX_BEAMER_RED_START;
888       else
889         return GFX_EMPTY;
890
891       return base_graphic + element_phase;
892     }
893   }
894 }
895
896 void RedrawPlayfield_MM()
897 {
898   DrawLevel_MM();
899 }
900
901 void BlitScreenToBitmap_MM(Bitmap *target_bitmap)
902 {
903   BlitBitmap(drawto_field, target_bitmap, 0, 0, SXSIZE, SYSIZE, SX, SY);
904 }