added overlay graphics for virtual buttons for touch devices (Android)
[rocksndiamonds.git] / src / tools.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // tools.c
10 // ============================================================================
11
12 #include <math.h>
13
14 #include "libgame/libgame.h"
15
16 #include "tools.h"
17 #include "init.h"
18 #include "game.h"
19 #include "events.h"
20 #include "anim.h"
21 #include "network.h"
22 #include "tape.h"
23 #include "screens.h"
24
25
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX            FALSE
28 #define DEBUG_FRAME_TIME        FALSE
29
30 /* tool button identifiers */
31 #define TOOL_CTRL_ID_YES        0
32 #define TOOL_CTRL_ID_NO         1
33 #define TOOL_CTRL_ID_CONFIRM    2
34 #define TOOL_CTRL_ID_PLAYER_1   3
35 #define TOOL_CTRL_ID_PLAYER_2   4
36 #define TOOL_CTRL_ID_PLAYER_3   5
37 #define TOOL_CTRL_ID_PLAYER_4   6
38
39 #define NUM_TOOL_BUTTONS        7
40
41 /* constants for number of doors and door parts */
42 #define NUM_DOORS               2
43 #define NUM_PANELS              NUM_DOORS
44 // #define NUM_PANELS           0
45 #define MAX_PARTS_PER_DOOR      8
46 #define MAX_DOOR_PARTS          (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
47 #define DOOR_PART_IS_PANEL(i)   ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
48
49
50 struct DoorPartOrderInfo
51 {
52   int nr;
53   int sort_priority;
54 };
55
56 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
57
58 struct DoorPartControlInfo
59 {
60   int door_token;
61   int graphic;
62   struct DoorPartPosInfo *pos;
63 };
64
65 static struct DoorPartControlInfo door_part_controls[] =
66 {
67   {
68     DOOR_1,
69     IMG_GFX_DOOR_1_PART_1,
70     &door_1.part_1
71   },
72   {
73     DOOR_1,
74     IMG_GFX_DOOR_1_PART_2,
75     &door_1.part_2
76   },
77   {
78     DOOR_1,
79     IMG_GFX_DOOR_1_PART_3,
80     &door_1.part_3
81   },
82   {
83     DOOR_1,
84     IMG_GFX_DOOR_1_PART_4,
85     &door_1.part_4
86   },
87   {
88     DOOR_1,
89     IMG_GFX_DOOR_1_PART_5,
90     &door_1.part_5
91   },
92   {
93     DOOR_1,
94     IMG_GFX_DOOR_1_PART_6,
95     &door_1.part_6
96   },
97   {
98     DOOR_1,
99     IMG_GFX_DOOR_1_PART_7,
100     &door_1.part_7
101   },
102   {
103     DOOR_1,
104     IMG_GFX_DOOR_1_PART_8,
105     &door_1.part_8
106   },
107
108   {
109     DOOR_2,
110     IMG_GFX_DOOR_2_PART_1,
111     &door_2.part_1
112   },
113   {
114     DOOR_2,
115     IMG_GFX_DOOR_2_PART_2,
116     &door_2.part_2
117   },
118   {
119     DOOR_2,
120     IMG_GFX_DOOR_2_PART_3,
121     &door_2.part_3
122   },
123   {
124     DOOR_2,
125     IMG_GFX_DOOR_2_PART_4,
126     &door_2.part_4
127   },
128   {
129     DOOR_2,
130     IMG_GFX_DOOR_2_PART_5,
131     &door_2.part_5
132   },
133   {
134     DOOR_2,
135     IMG_GFX_DOOR_2_PART_6,
136     &door_2.part_6
137   },
138   {
139     DOOR_2,
140     IMG_GFX_DOOR_2_PART_7,
141     &door_2.part_7
142   },
143   {
144     DOOR_2,
145     IMG_GFX_DOOR_2_PART_8,
146     &door_2.part_8
147   },
148
149   {
150     DOOR_1,
151     IMG_BACKGROUND_PANEL,
152     &door_1.panel
153   },
154   {
155     DOOR_2,
156     IMG_BACKGROUND_TAPE,
157     &door_2.panel
158   },
159
160   {
161     -1,
162     -1,
163     NULL
164   }
165 };
166
167
168 /* forward declaration for internal use */
169 static void UnmapToolButtons();
170 static void HandleToolButtons(struct GadgetInfo *);
171 static int el_act_dir2crm(int, int, int);
172 static int el_act2crm(int, int);
173
174 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
175 static int request_gadget_id = -1;
176
177 static char *print_if_not_empty(int element)
178 {
179   static char *s = NULL;
180   char *token_name = element_info[element].token_name;
181
182   if (s != NULL)
183     free(s);
184
185   s = checked_malloc(strlen(token_name) + 10 + 1);
186
187   if (element != EL_EMPTY)
188     sprintf(s, "%d\t['%s']", element, token_name);
189   else
190     sprintf(s, "%d", element);
191
192   return s;
193 }
194
195 int correctLevelPosX_EM(int lx)
196 {
197   lx -= 1;
198   lx -= (BorderElement != EL_EMPTY ? 1 : 0);
199
200   return lx;
201 }
202
203 int correctLevelPosY_EM(int ly)
204 {
205   ly -= 1;
206   ly -= (BorderElement != EL_EMPTY ? 1 : 0);
207
208   return ly;
209 }
210
211 static int getFieldbufferOffsetX_RND()
212 {
213   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
214   int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
215   int dx_var = dx * TILESIZE_VAR / TILESIZE;
216   int fx = FX;
217
218   if (EVEN(SCR_FIELDX))
219   {
220     int ffx = (scroll_x - SBX_Left)  * TILEX_VAR + dx_var;
221
222     if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
223       fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
224     else
225       fx += (dx_var > 0 ? TILEX_VAR : 0);
226   }
227   else
228   {
229     fx += dx_var;
230   }
231
232   if (full_lev_fieldx <= SCR_FIELDX)
233   {
234     if (EVEN(SCR_FIELDX))
235       fx = 2 * TILEX_VAR - (ODD(lev_fieldx)  ? TILEX_VAR / 2 : 0);
236     else
237       fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
238   }
239
240   return fx;
241 }
242
243 static int getFieldbufferOffsetY_RND()
244 {
245   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
246   int dy = (ScreenMovDir & (MV_UP | MV_DOWN)    ? ScreenGfxPos : 0);
247   int dy_var = dy * TILESIZE_VAR / TILESIZE;
248   int fy = FY;
249
250   if (EVEN(SCR_FIELDY))
251   {
252     int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
253
254     if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
255       fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
256     else
257       fy += (dy_var > 0 ? TILEY_VAR : 0);
258   }
259   else
260   {
261     fy += dy_var;
262   }
263
264   if (full_lev_fieldy <= SCR_FIELDY)
265   {
266     if (EVEN(SCR_FIELDY))
267       fy = 2 * TILEY_VAR - (ODD(lev_fieldy)  ? TILEY_VAR / 2 : 0);
268     else
269       fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
270   }
271
272   return fy;
273 }
274
275 static int getLevelFromScreenX_RND(int sx)
276 {
277   int fx = getFieldbufferOffsetX_RND();
278   int dx = fx - FX;
279   int px = sx - SX;
280   int lx = LEVELX((px + dx) / TILESIZE_VAR);
281
282   return lx;
283 }
284
285 static int getLevelFromScreenY_RND(int sy)
286 {
287   int fy = getFieldbufferOffsetY_RND();
288   int dy = fy - FY;
289   int py = sy - SY;
290   int ly = LEVELY((py + dy) / TILESIZE_VAR);
291
292   return ly;
293 }
294
295 static int getLevelFromScreenX_EM(int sx)
296 {
297   int level_xsize = level.native_em_level->lev->width;
298   int full_xsize = level_xsize * TILESIZE_VAR;
299
300   sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
301
302   int fx = getFieldbufferOffsetX_EM();
303   int dx = fx;
304   int px = sx - SX;
305   int lx = LEVELX((px + dx) / TILESIZE_VAR);
306
307   lx = correctLevelPosX_EM(lx);
308
309   return lx;
310 }
311
312 static int getLevelFromScreenY_EM(int sy)
313 {
314   int level_ysize = level.native_em_level->lev->height;
315   int full_ysize = level_ysize * TILESIZE_VAR;
316
317   sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
318
319   int fy = getFieldbufferOffsetY_EM();
320   int dy = fy;
321   int py = sy - SY;
322   int ly = LEVELY((py + dy) / TILESIZE_VAR);
323
324   ly = correctLevelPosY_EM(ly);
325
326   return ly;
327 }
328
329 static int getLevelFromScreenX_SP(int sx)
330 {
331   int menBorder = setup.sp_show_border_elements;
332   int level_xsize = level.native_sp_level->width;
333   int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
334
335   sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
336
337   int fx = getFieldbufferOffsetX_SP();
338   int dx = fx - FX;
339   int px = sx - SX;
340   int lx = LEVELX((px + dx) / TILESIZE_VAR);
341
342   return lx;
343 }
344
345 static int getLevelFromScreenY_SP(int sy)
346 {
347   int menBorder = setup.sp_show_border_elements;
348   int level_ysize = level.native_sp_level->height;
349   int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
350
351   sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
352
353   int fy = getFieldbufferOffsetY_SP();
354   int dy = fy - FY;
355   int py = sy - SY;
356   int ly = LEVELY((py + dy) / TILESIZE_VAR);
357
358   return ly;
359 }
360
361 int getLevelFromScreenX(int x)
362 {
363   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
364     return getLevelFromScreenX_EM(x);
365   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
366     return getLevelFromScreenX_SP(x);
367   else
368     return getLevelFromScreenX_RND(x);
369 }
370
371 int getLevelFromScreenY(int y)
372 {
373   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
374     return getLevelFromScreenY_EM(y);
375   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
376     return getLevelFromScreenY_SP(y);
377   else
378     return getLevelFromScreenY_RND(y);
379 }
380
381 void DumpTile(int x, int y)
382 {
383   int sx = SCREENX(x);
384   int sy = SCREENY(y);
385
386   printf_line("-", 79);
387   printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
388   printf_line("-", 79);
389
390   if (!IN_LEV_FIELD(x, y))
391   {
392     printf("(not in level field)\n");
393     printf("\n");
394
395     return;
396   }
397
398   printf("  Feld:        %d\t['%s']\n", Feld[x][y],
399          element_info[Feld[x][y]].token_name);
400   printf("  Back:        %s\n", print_if_not_empty(Back[x][y]));
401   printf("  Store:       %s\n", print_if_not_empty(Store[x][y]));
402   printf("  Store2:      %s\n", print_if_not_empty(Store2[x][y]));
403   printf("  StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
404   printf("  MovPos:      %d\n", MovPos[x][y]);
405   printf("  MovDir:      %d\n", MovDir[x][y]);
406   printf("  MovDelay:    %d\n", MovDelay[x][y]);
407   printf("  ChangeDelay: %d\n", ChangeDelay[x][y]);
408   printf("  CustomValue: %d\n", CustomValue[x][y]);
409   printf("  GfxElement:  %d\n", GfxElement[x][y]);
410   printf("  GfxAction:   %d\n", GfxAction[x][y]);
411   printf("  GfxFrame:    %d [%d]\n", GfxFrame[x][y], FrameCounter);
412   printf("  Player x/y:  %d, %d\n", local_player->jx, local_player->jy);
413   printf("\n");
414 }
415
416 void DumpTileFromScreen(int sx, int sy)
417 {
418   int lx = getLevelFromScreenX(sx);
419   int ly = getLevelFromScreenY(sy);
420
421   DumpTile(lx, ly);
422 }
423
424 void SetDrawtoField(int mode)
425 {
426   if (mode == DRAW_TO_FIELDBUFFER)
427   {
428     FX = 2 * TILEX_VAR;
429     FY = 2 * TILEY_VAR;
430     BX1 = -2;
431     BY1 = -2;
432     BX2 = SCR_FIELDX + 1;
433     BY2 = SCR_FIELDY + 1;
434
435     drawto_field = fieldbuffer;
436   }
437   else  /* DRAW_TO_BACKBUFFER */
438   {
439     FX = SX;
440     FY = SY;
441     BX1 = 0;
442     BY1 = 0;
443     BX2 = SCR_FIELDX - 1;
444     BY2 = SCR_FIELDY - 1;
445
446     drawto_field = backbuffer;
447   }
448 }
449
450 static void RedrawPlayfield_RND()
451 {
452   if (game.envelope_active)
453     return;
454
455   DrawLevel(REDRAW_ALL);
456   DrawAllPlayers();
457 }
458
459 void RedrawPlayfield()
460 {
461   if (game_status != GAME_MODE_PLAYING)
462     return;
463
464   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
465     RedrawPlayfield_EM(TRUE);
466   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
467     RedrawPlayfield_SP(TRUE);
468   else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
469     RedrawPlayfield_RND();
470
471   BlitScreenToBitmap(backbuffer);
472
473   BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
474              gfx.sx, gfx.sy);
475 }
476
477 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
478                                      int draw_target)
479 {
480   Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
481   Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
482
483   if (x == -1 && y == -1)
484     return;
485
486   if (draw_target == DRAW_TO_SCREEN)
487     BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
488   else
489     BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
490 }
491
492 static void DrawMaskedBorderExt_FIELD(int draw_target)
493 {
494   if (global.border_status >= GAME_MODE_MAIN &&
495       global.border_status <= GAME_MODE_PLAYING &&
496       border.draw_masked[global.border_status])
497     DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
498                              draw_target);
499 }
500
501 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
502 {
503   // when drawing to backbuffer, never draw border over open doors
504   if (draw_target == DRAW_TO_BACKBUFFER &&
505       (GetDoorState() & DOOR_OPEN_1))
506     return;
507
508   if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
509       (global.border_status != GAME_MODE_EDITOR ||
510        border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
511     DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
512 }
513
514 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
515 {
516   // when drawing to backbuffer, never draw border over open doors
517   if (draw_target == DRAW_TO_BACKBUFFER &&
518       (GetDoorState() & DOOR_OPEN_2))
519     return;
520
521   if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
522       global.border_status != GAME_MODE_EDITOR)
523     DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
524 }
525
526 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
527 {
528   /* currently not available */
529 }
530
531 static void DrawMaskedBorderExt_ALL(int draw_target)
532 {
533   DrawMaskedBorderExt_FIELD(draw_target);
534   DrawMaskedBorderExt_DOOR_1(draw_target);
535   DrawMaskedBorderExt_DOOR_2(draw_target);
536   DrawMaskedBorderExt_DOOR_3(draw_target);
537 }
538
539 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
540 {
541   /* never draw masked screen borders on borderless screens */
542   if (global.border_status == GAME_MODE_LOADING ||
543       global.border_status == GAME_MODE_TITLE)
544     return;
545
546   if (redraw_mask & REDRAW_ALL)
547     DrawMaskedBorderExt_ALL(draw_target);
548   else
549   {
550     if (redraw_mask & REDRAW_FIELD)
551       DrawMaskedBorderExt_FIELD(draw_target);
552     if (redraw_mask & REDRAW_DOOR_1)
553       DrawMaskedBorderExt_DOOR_1(draw_target);
554     if (redraw_mask & REDRAW_DOOR_2)
555       DrawMaskedBorderExt_DOOR_2(draw_target);
556     if (redraw_mask & REDRAW_DOOR_3)
557       DrawMaskedBorderExt_DOOR_3(draw_target);
558   }
559 }
560
561 void DrawMaskedBorder_FIELD()
562 {
563   DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
564 }
565
566 void DrawMaskedBorder(int redraw_mask)
567 {
568   DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
569 }
570
571 void DrawMaskedBorderToTarget(int draw_target)
572 {
573   if (draw_target == DRAW_TO_BACKBUFFER ||
574       draw_target == DRAW_TO_SCREEN)
575   {
576     DrawMaskedBorderExt(REDRAW_ALL, draw_target);
577   }
578   else
579   {
580     int last_border_status = global.border_status;
581
582     if (draw_target == DRAW_TO_FADE_SOURCE)
583     {
584       global.border_status = gfx.fade_border_source_status;
585       gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
586     }
587     else if (draw_target == DRAW_TO_FADE_TARGET)
588     {
589       global.border_status = gfx.fade_border_target_status;
590       gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
591     }
592
593     DrawMaskedBorderExt(REDRAW_ALL, draw_target);
594
595     global.border_status = last_border_status;
596     gfx.masked_border_bitmap_ptr = backbuffer;
597   }
598 }
599
600 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
601 {
602   int fx = getFieldbufferOffsetX_RND();
603   int fy = getFieldbufferOffsetY_RND();
604
605   BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
606 }
607
608 void BlitScreenToBitmap(Bitmap *target_bitmap)
609 {
610   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
611     BlitScreenToBitmap_EM(target_bitmap);
612   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
613     BlitScreenToBitmap_SP(target_bitmap);
614   else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
615     BlitScreenToBitmap_RND(target_bitmap);
616
617   redraw_mask |= REDRAW_FIELD;
618 }
619
620 void DrawFramesPerSecond()
621 {
622   char text[100];
623   int font_nr = FONT_TEXT_2;
624   int font_width = getFontWidth(font_nr);
625
626   sprintf(text, "%04.1f fps", global.frames_per_second);
627
628   DrawTextExt(backbuffer, WIN_XSIZE - font_width * strlen(text), 0, text,
629               font_nr, BLIT_OPAQUE);
630 }
631
632 #if DEBUG_FRAME_TIME
633 static void PrintFrameTimeDebugging()
634 {
635   static unsigned int last_counter = 0;
636   unsigned int counter = Counter();
637   int diff_1 = counter - last_counter;
638   int diff_2 = diff_1 - GAME_FRAME_DELAY;
639   int diff_2_max = 20;
640   int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
641   char diff_bar[2 * diff_2_max + 5];
642   int pos = 0;
643   int i;
644
645   diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
646
647   for (i = 0; i < diff_2_max; i++)
648     diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
649                        i >= diff_2_max - diff_2_cut ? '-' : ' ');
650
651   diff_bar[pos++] = '|';
652
653   for (i = 0; i < diff_2_max; i++)
654     diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
655
656   diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
657
658   diff_bar[pos++] = '\0';
659
660   Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
661         counter,
662         diff_1,
663         (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
664         diff_bar);
665
666   last_counter = counter;
667 }
668 #endif
669
670 static int unifiedRedrawMask(int mask)
671 {
672   if (mask & REDRAW_ALL)
673     return REDRAW_ALL;
674
675   if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
676     return REDRAW_ALL;
677
678   return mask;
679 }
680
681 static boolean equalRedrawMasks(int mask_1, int mask_2)
682 {
683   return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
684 }
685
686 void BackToFront()
687 {
688   static int last_redraw_mask = REDRAW_NONE;
689
690   // force screen redraw in every frame to continue drawing global animations
691   // (but always use the last redraw mask to prevent unwanted side effects)
692   if (redraw_mask == REDRAW_NONE)
693     redraw_mask = last_redraw_mask;
694
695   last_redraw_mask = redraw_mask;
696
697 #if 1
698   // masked border now drawn immediately when blitting backbuffer to window
699 #else
700   // draw masked border to all viewports, if defined
701   DrawMaskedBorder(redraw_mask);
702 #endif
703
704   // draw frames per second (only if debug mode is enabled)
705   if (redraw_mask & REDRAW_FPS)
706     DrawFramesPerSecond();
707
708   // remove playfield redraw before potentially merging with doors redraw
709   if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
710     redraw_mask &= ~REDRAW_FIELD;
711
712   // redraw complete window if both playfield and (some) doors need redraw
713   if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
714     redraw_mask = REDRAW_ALL;
715
716   /* although redrawing the whole window would be fine for normal gameplay,
717      being able to only redraw the playfield is required for deactivating
718      certain drawing areas (mainly playfield) to work, which is needed for
719      warp-forward to be fast enough (by skipping redraw of most frames) */
720
721   if (redraw_mask & REDRAW_ALL)
722   {
723     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
724   }
725   else if (redraw_mask & REDRAW_FIELD)
726   {
727     BlitBitmap(backbuffer, window,
728                REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
729   }
730   else if (redraw_mask & REDRAW_DOORS)
731   {
732     // merge door areas to prevent calling screen redraw more than once
733     int x1 = WIN_XSIZE;
734     int y1 = WIN_YSIZE;
735     int x2 = 0;
736     int y2 = 0;
737
738     if (redraw_mask & REDRAW_DOOR_1)
739     {
740       x1 = MIN(x1, DX);
741       y1 = MIN(y1, DY);
742       x2 = MAX(x2, DX + DXSIZE);
743       y2 = MAX(y2, DY + DYSIZE);
744     }
745
746     if (redraw_mask & REDRAW_DOOR_2)
747     {
748       x1 = MIN(x1, VX);
749       y1 = MIN(y1, VY);
750       x2 = MAX(x2, VX + VXSIZE);
751       y2 = MAX(y2, VY + VYSIZE);
752     }
753
754     if (redraw_mask & REDRAW_DOOR_3)
755     {
756       x1 = MIN(x1, EX);
757       y1 = MIN(y1, EY);
758       x2 = MAX(x2, EX + EXSIZE);
759       y2 = MAX(y2, EY + EYSIZE);
760     }
761
762     BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
763   }
764
765   redraw_mask = REDRAW_NONE;
766
767 #if DEBUG_FRAME_TIME
768   PrintFrameTimeDebugging();
769 #endif
770 }
771
772 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
773 {
774   unsigned int frame_delay_value_old = GetVideoFrameDelay();
775
776   SetVideoFrameDelay(frame_delay_value);
777
778   BackToFront();
779
780   SetVideoFrameDelay(frame_delay_value_old);
781 }
782
783 static int fade_type_skip = FADE_TYPE_NONE;
784
785 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
786 {
787   void (*draw_border_function)(void) = NULL;
788   int x, y, width, height;
789   int fade_delay, post_delay;
790
791   if (fade_type == FADE_TYPE_FADE_OUT)
792   {
793     if (fade_type_skip != FADE_TYPE_NONE)
794     {
795       /* skip all fade operations until specified fade operation */
796       if (fade_type & fade_type_skip)
797         fade_type_skip = FADE_TYPE_NONE;
798
799       return;
800     }
801
802     if (fading.fade_mode & FADE_TYPE_TRANSFORM)
803       return;
804   }
805
806   redraw_mask |= fade_mask;
807
808   if (fade_type == FADE_TYPE_SKIP)
809   {
810     fade_type_skip = fade_mode;
811
812     return;
813   }
814
815   fade_delay = fading.fade_delay;
816   post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
817
818   if (fade_type_skip != FADE_TYPE_NONE)
819   {
820     /* skip all fade operations until specified fade operation */
821     if (fade_type & fade_type_skip)
822       fade_type_skip = FADE_TYPE_NONE;
823
824     fade_delay = 0;
825   }
826
827   if (global.autoplay_leveldir)
828   {
829     return;
830   }
831
832   if (fade_mask == REDRAW_FIELD)
833   {
834     x = FADE_SX;
835     y = FADE_SY;
836     width  = FADE_SXSIZE;
837     height = FADE_SYSIZE;
838
839     if (border.draw_masked_when_fading)
840       draw_border_function = DrawMaskedBorder_FIELD;    /* update when fading */
841     else
842       DrawMaskedBorder_FIELD();                         /* draw once */
843   }
844   else          /* REDRAW_ALL */
845   {
846     x = 0;
847     y = 0;
848     width  = WIN_XSIZE;
849     height = WIN_YSIZE;
850   }
851
852   if (!setup.fade_screens ||
853       fade_delay == 0 ||
854       fading.fade_mode == FADE_MODE_NONE)
855   {
856     if (fade_mode == FADE_MODE_FADE_OUT)
857       return;
858
859     BlitBitmap(backbuffer, window, x, y, width, height, x, y);
860
861     redraw_mask &= ~fade_mask;
862
863     return;
864   }
865
866   FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
867                 draw_border_function);
868
869   redraw_mask &= ~fade_mask;
870 }
871
872 static void SetScreenStates_BeforeFadingIn()
873 {
874   // temporarily set screen mode for animations to screen after fading in
875   global.anim_status = global.anim_status_next;
876
877   // store backbuffer with all animations that will be started after fading in
878   if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
879     PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
880
881   // set screen mode for animations back to fading
882   global.anim_status = GAME_MODE_PSEUDO_FADING;
883 }
884
885 static void SetScreenStates_AfterFadingIn()
886 {
887   // store new source screen (to use correct masked border for fading)
888   gfx.fade_border_source_status = global.border_status;
889
890   global.anim_status = global.anim_status_next;
891 }
892
893 static void SetScreenStates_BeforeFadingOut()
894 {
895   // store new target screen (to use correct masked border for fading)
896   gfx.fade_border_target_status = game_status;
897
898   // set screen mode for animations to fading
899   global.anim_status = GAME_MODE_PSEUDO_FADING;
900
901   // store backbuffer with all animations that will be stopped for fading out
902   if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
903     PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
904 }
905
906 static void SetScreenStates_AfterFadingOut()
907 {
908   global.border_status = game_status;
909 }
910
911 void FadeIn(int fade_mask)
912 {
913   SetScreenStates_BeforeFadingIn();
914
915 #if 1
916   DrawMaskedBorder(REDRAW_ALL);
917 #endif
918
919   if (fading.fade_mode & FADE_TYPE_TRANSFORM)
920     FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
921   else
922     FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
923
924   FADE_SX = REAL_SX;
925   FADE_SY = REAL_SY;
926   FADE_SXSIZE = FULL_SXSIZE;
927   FADE_SYSIZE = FULL_SYSIZE;
928
929   if (game_status == GAME_MODE_PLAYING &&
930       strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
931     SetOverlayActive(TRUE);
932
933   SetScreenStates_AfterFadingIn();
934
935   // force update of global animation status in case of rapid screen changes
936   redraw_mask = REDRAW_ALL;
937   BackToFront();
938 }
939
940 void FadeOut(int fade_mask)
941 {
942   // update screen if areas covered by "fade_mask" and "redraw_mask" differ
943   if (!equalRedrawMasks(fade_mask, redraw_mask))
944     BackToFront();
945
946   SetScreenStates_BeforeFadingOut();
947
948   SetOverlayActive(FALSE);
949
950 #if 0
951   DrawMaskedBorder(REDRAW_ALL);
952 #endif
953
954   if (fading.fade_mode & FADE_TYPE_TRANSFORM)
955     FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
956   else
957     FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
958
959   SetScreenStates_AfterFadingOut();
960 }
961
962 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
963 {
964   static struct TitleFadingInfo fading_leave_stored;
965
966   if (set)
967     fading_leave_stored = fading_leave;
968   else
969     fading = fading_leave_stored;
970 }
971
972 void FadeSetEnterMenu()
973 {
974   fading = menu.enter_menu;
975
976   FadeSetLeaveNext(fading, TRUE);       /* (keep same fade mode) */
977 }
978
979 void FadeSetLeaveMenu()
980 {
981   fading = menu.leave_menu;
982
983   FadeSetLeaveNext(fading, TRUE);       /* (keep same fade mode) */
984 }
985
986 void FadeSetEnterScreen()
987 {
988   fading = menu.enter_screen[game_status];
989
990   FadeSetLeaveNext(menu.leave_screen[game_status], TRUE);       /* store */
991 }
992
993 void FadeSetNextScreen()
994 {
995   fading = menu.next_screen[game_status];
996
997   // (do not overwrite fade mode set by FadeSetEnterScreen)
998   // FadeSetLeaveNext(fading, TRUE);    /* (keep same fade mode) */
999 }
1000
1001 void FadeSetLeaveScreen()
1002 {
1003   FadeSetLeaveNext(menu.leave_screen[game_status], FALSE);      /* recall */
1004 }
1005
1006 void FadeSetFromType(int type)
1007 {
1008   if (type & TYPE_ENTER_SCREEN)
1009     FadeSetEnterScreen();
1010   else if (type & TYPE_ENTER)
1011     FadeSetEnterMenu();
1012   else if (type & TYPE_LEAVE)
1013     FadeSetLeaveMenu();
1014 }
1015
1016 void FadeSetDisabled()
1017 {
1018   static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1019
1020   fading = fading_none;
1021 }
1022
1023 void FadeSkipNextFadeIn()
1024 {
1025   FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1026 }
1027
1028 void FadeSkipNextFadeOut()
1029 {
1030   FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1031 }
1032
1033 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1034 {
1035   boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1036
1037   return (graphic == IMG_UNDEFINED ? NULL :
1038           graphic_info[graphic].bitmap != NULL || redefined ?
1039           graphic_info[graphic].bitmap :
1040           graphic_info[default_graphic].bitmap);
1041 }
1042
1043 Bitmap *getBackgroundBitmap(int graphic)
1044 {
1045   return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1046 }
1047
1048 Bitmap *getGlobalBorderBitmap(int graphic)
1049 {
1050   return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1051 }
1052
1053 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1054 {
1055   int graphic =
1056     (status == GAME_MODE_MAIN ||
1057      status == GAME_MODE_PSEUDO_TYPENAME        ? IMG_GLOBAL_BORDER_MAIN :
1058      status == GAME_MODE_SCORES                 ? IMG_GLOBAL_BORDER_SCORES :
1059      status == GAME_MODE_EDITOR                 ? IMG_GLOBAL_BORDER_EDITOR :
1060      status == GAME_MODE_PLAYING                ? IMG_GLOBAL_BORDER_PLAYING :
1061      IMG_GLOBAL_BORDER);
1062
1063   return getGlobalBorderBitmap(graphic);
1064 }
1065
1066 void SetWindowBackgroundImageIfDefined(int graphic)
1067 {
1068   if (graphic_info[graphic].bitmap)
1069     SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1070 }
1071
1072 void SetMainBackgroundImageIfDefined(int graphic)
1073 {
1074   if (graphic_info[graphic].bitmap)
1075     SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1076 }
1077
1078 void SetDoorBackgroundImageIfDefined(int graphic)
1079 {
1080   if (graphic_info[graphic].bitmap)
1081     SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1082 }
1083
1084 void SetWindowBackgroundImage(int graphic)
1085 {
1086   SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1087 }
1088
1089 void SetMainBackgroundImage(int graphic)
1090 {
1091   SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1092 }
1093
1094 void SetDoorBackgroundImage(int graphic)
1095 {
1096   SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1097 }
1098
1099 void SetPanelBackground()
1100 {
1101   struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1102
1103   BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1104                   gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1105
1106   SetDoorBackgroundBitmap(bitmap_db_panel);
1107 }
1108
1109 void DrawBackground(int x, int y, int width, int height)
1110 {
1111   /* "drawto" might still point to playfield buffer here (hall of fame) */
1112   ClearRectangleOnBackground(backbuffer, x, y, width, height);
1113
1114   if (IN_GFX_FIELD_FULL(x, y))
1115     redraw_mask |= REDRAW_FIELD;
1116   else if (IN_GFX_DOOR_1(x, y))
1117     redraw_mask |= REDRAW_DOOR_1;
1118   else if (IN_GFX_DOOR_2(x, y))
1119     redraw_mask |= REDRAW_DOOR_2;
1120   else if (IN_GFX_DOOR_3(x, y))
1121     redraw_mask |= REDRAW_DOOR_3;
1122 }
1123
1124 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1125 {
1126   struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1127
1128   if (font->bitmap == NULL)
1129     return;
1130
1131   DrawBackground(x, y, width, height);
1132 }
1133
1134 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1135 {
1136   struct GraphicInfo *g = &graphic_info[graphic];
1137
1138   if (g->bitmap == NULL)
1139     return;
1140
1141   DrawBackground(x, y, width, height);
1142 }
1143
1144 static int game_status_last = -1;
1145 static Bitmap *global_border_bitmap_last = NULL;
1146 static Bitmap *global_border_bitmap = NULL;
1147 static int real_sx_last = -1, real_sy_last = -1;
1148 static int full_sxsize_last = -1, full_sysize_last = -1;
1149 static int dx_last = -1, dy_last = -1;
1150 static int dxsize_last = -1, dysize_last = -1;
1151 static int vx_last = -1, vy_last = -1;
1152 static int vxsize_last = -1, vysize_last = -1;
1153
1154 boolean CheckIfGlobalBorderHasChanged()
1155 {
1156   // if game status has not changed, global border has not changed either
1157   if (game_status == game_status_last)
1158     return FALSE;
1159
1160   // determine and store new global border bitmap for current game status
1161   global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1162
1163   return (global_border_bitmap_last != global_border_bitmap);
1164 }
1165
1166 boolean CheckIfGlobalBorderRedrawIsNeeded()
1167 {
1168   // if game status has not changed, nothing has to be redrawn
1169   if (game_status == game_status_last)
1170     return FALSE;
1171
1172   // redraw if last screen was title screen
1173   if (game_status_last == GAME_MODE_TITLE)
1174     return TRUE;
1175
1176   // redraw if global screen border has changed
1177   if (CheckIfGlobalBorderHasChanged())
1178     return TRUE;
1179
1180   // redraw if position or size of playfield area has changed
1181   if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1182       full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1183     return TRUE;
1184
1185   // redraw if position or size of door area has changed
1186   if (dx_last != DX || dy_last != DY ||
1187       dxsize_last != DXSIZE || dysize_last != DYSIZE)
1188     return TRUE;
1189
1190   // redraw if position or size of tape area has changed
1191   if (vx_last != VX || vy_last != VY ||
1192       vxsize_last != VXSIZE || vysize_last != VYSIZE)
1193     return TRUE;
1194
1195   return FALSE;
1196 }
1197
1198 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1199 {
1200   if (bitmap)
1201     BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1202   else
1203     ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1204 }
1205
1206 void RedrawGlobalBorder()
1207 {
1208   Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1209
1210   RedrawGlobalBorderFromBitmap(bitmap);
1211
1212   redraw_mask = REDRAW_ALL;
1213 }
1214
1215 static void RedrawGlobalBorderIfNeeded()
1216 {
1217   if (game_status == game_status_last)
1218     return;
1219
1220   // copy current draw buffer to later copy back areas that have not changed
1221   if (game_status_last != GAME_MODE_TITLE)
1222     BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1223
1224   if (CheckIfGlobalBorderRedrawIsNeeded())
1225   {
1226     // redraw global screen border (or clear, if defined to be empty)
1227     RedrawGlobalBorderFromBitmap(global_border_bitmap);
1228
1229     // copy previous playfield and door areas, if they are defined on both
1230     // previous and current screen and if they still have the same size
1231
1232     if (real_sx_last != -1 && real_sy_last != -1 &&
1233         REAL_SX != -1 && REAL_SY != -1 &&
1234         full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1235       BlitBitmap(bitmap_db_store_1, backbuffer,
1236                  real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1237                  REAL_SX, REAL_SY);
1238
1239     if (dx_last != -1 && dy_last != -1 &&
1240         DX != -1 && DY != -1 &&
1241         dxsize_last == DXSIZE && dysize_last == DYSIZE)
1242       BlitBitmap(bitmap_db_store_1, backbuffer,
1243                  dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1244
1245     if (vx_last != -1 && vy_last != -1 &&
1246         VX != -1 && VY != -1 &&
1247         vxsize_last == VXSIZE && vysize_last == VYSIZE)
1248       BlitBitmap(bitmap_db_store_1, backbuffer,
1249                  vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1250
1251     redraw_mask = REDRAW_ALL;
1252   }
1253
1254   game_status_last = game_status;
1255
1256   global_border_bitmap_last = global_border_bitmap;
1257
1258   real_sx_last = REAL_SX;
1259   real_sy_last = REAL_SY;
1260   full_sxsize_last = FULL_SXSIZE;
1261   full_sysize_last = FULL_SYSIZE;
1262   dx_last = DX;
1263   dy_last = DY;
1264   dxsize_last = DXSIZE;
1265   dysize_last = DYSIZE;
1266   vx_last = VX;
1267   vy_last = VY;
1268   vxsize_last = VXSIZE;
1269   vysize_last = VYSIZE;
1270 }
1271
1272 void ClearField()
1273 {
1274   RedrawGlobalBorderIfNeeded();
1275
1276   /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1277   /* (when entering hall of fame after playing) */
1278   DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1279
1280   /* !!! maybe this should be done before clearing the background !!! */
1281   if (game_status == GAME_MODE_PLAYING)
1282   {
1283     ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1284     SetDrawtoField(DRAW_TO_FIELDBUFFER);
1285   }
1286   else
1287   {
1288     SetDrawtoField(DRAW_TO_BACKBUFFER);
1289   }
1290 }
1291
1292 void MarkTileDirty(int x, int y)
1293 {
1294   redraw_mask |= REDRAW_FIELD;
1295 }
1296
1297 void SetBorderElement()
1298 {
1299   int x, y;
1300
1301   BorderElement = EL_EMPTY;
1302
1303   for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1304   {
1305     for (x = 0; x < lev_fieldx; x++)
1306     {
1307       if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1308         BorderElement = EL_STEELWALL;
1309
1310       if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1311         x = lev_fieldx - 2;
1312     }
1313   }
1314 }
1315
1316 void FloodFillLevel(int from_x, int from_y, int fill_element,
1317                     short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1318                     int max_fieldx, int max_fieldy)
1319 {
1320   int i,x,y;
1321   int old_element;
1322   static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1323   static int safety = 0;
1324
1325   /* check if starting field still has the desired content */
1326   if (field[from_x][from_y] == fill_element)
1327     return;
1328
1329   safety++;
1330
1331   if (safety > max_fieldx * max_fieldy)
1332     Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1333
1334   old_element = field[from_x][from_y];
1335   field[from_x][from_y] = fill_element;
1336
1337   for (i = 0; i < 4; i++)
1338   {
1339     x = from_x + check[i][0];
1340     y = from_y + check[i][1];
1341
1342     if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1343       FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1344   }
1345
1346   safety--;
1347 }
1348
1349 void SetRandomAnimationValue(int x, int y)
1350 {
1351   gfx.anim_random_frame = GfxRandom[x][y];
1352 }
1353
1354 int getGraphicAnimationFrame(int graphic, int sync_frame)
1355 {
1356   /* animation synchronized with global frame counter, not move position */
1357   if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1358     sync_frame = FrameCounter;
1359
1360   return getAnimationFrame(graphic_info[graphic].anim_frames,
1361                            graphic_info[graphic].anim_delay,
1362                            graphic_info[graphic].anim_mode,
1363                            graphic_info[graphic].anim_start_frame,
1364                            sync_frame);
1365 }
1366
1367 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1368 {
1369   struct GraphicInfo *g = &graphic_info[graphic];
1370   int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1371
1372   if (tilesize == gfx.standard_tile_size)
1373     *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1374   else if (tilesize == game.tile_size)
1375     *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1376   else
1377     *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1378 }
1379
1380 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1381                         boolean get_backside)
1382 {
1383   struct GraphicInfo *g = &graphic_info[graphic];
1384   int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1385   int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1386
1387   if (g->offset_y == 0)         /* frames are ordered horizontally */
1388   {
1389     int max_width = g->anim_frames_per_line * g->width;
1390     int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1391
1392     *x = pos % max_width;
1393     *y = src_y % g->height + pos / max_width * g->height;
1394   }
1395   else if (g->offset_x == 0)    /* frames are ordered vertically */
1396   {
1397     int max_height = g->anim_frames_per_line * g->height;
1398     int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1399
1400     *x = src_x % g->width + pos / max_height * g->width;
1401     *y = pos % max_height;
1402   }
1403   else                          /* frames are ordered diagonally */
1404   {
1405     *x = src_x + frame * g->offset_x;
1406     *y = src_y + frame * g->offset_y;
1407   }
1408 }
1409
1410 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1411                               Bitmap **bitmap, int *x, int *y,
1412                               boolean get_backside)
1413 {
1414   struct GraphicInfo *g = &graphic_info[graphic];
1415
1416   // if no in-game graphics defined, always use standard graphic size
1417   if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1418     tilesize = TILESIZE;
1419
1420   getGraphicSourceBitmap(graphic, tilesize, bitmap);
1421   getGraphicSourceXY(graphic, frame, x, y, get_backside);
1422
1423   *x = *x * tilesize / g->tile_size;
1424   *y = *y * tilesize / g->tile_size;
1425 }
1426
1427 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1428                               int *x, int *y, boolean get_backside)
1429 {
1430   getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1431                            get_backside);
1432 }
1433
1434 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1435                            Bitmap **bitmap, int *x, int *y)
1436 {
1437   getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1438 }
1439
1440 void getFixedGraphicSource(int graphic, int frame,
1441                            Bitmap **bitmap, int *x, int *y)
1442 {
1443   getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1444 }
1445
1446 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1447 {
1448   getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1449 }
1450
1451 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1452                                        int *x, int *y, boolean get_backside)
1453 {
1454   getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1455                            get_backside);
1456 }
1457
1458 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1459 {
1460   getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1461 }
1462
1463 void DrawGraphic(int x, int y, int graphic, int frame)
1464 {
1465 #if DEBUG
1466   if (!IN_SCR_FIELD(x, y))
1467   {
1468     printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1469     printf("DrawGraphic(): This should never happen!\n");
1470     return;
1471   }
1472 #endif
1473
1474   DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1475                  frame);
1476
1477   MarkTileDirty(x, y);
1478 }
1479
1480 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1481 {
1482 #if DEBUG
1483   if (!IN_SCR_FIELD(x, y))
1484   {
1485     printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1486     printf("DrawGraphic(): This should never happen!\n");
1487     return;
1488   }
1489 #endif
1490
1491   DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1492                       frame);
1493   MarkTileDirty(x, y);
1494 }
1495
1496 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1497                     int frame)
1498 {
1499   Bitmap *src_bitmap;
1500   int src_x, src_y;
1501
1502   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1503
1504   BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1505 }
1506
1507 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1508                          int frame)
1509 {
1510   Bitmap *src_bitmap;
1511   int src_x, src_y;
1512
1513   getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1514   BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1515 }
1516
1517 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1518 {
1519 #if DEBUG
1520   if (!IN_SCR_FIELD(x, y))
1521   {
1522     printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1523     printf("DrawGraphicThruMask(): This should never happen!\n");
1524     return;
1525   }
1526 #endif
1527
1528   DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1529                          graphic, frame);
1530
1531   MarkTileDirty(x, y);
1532 }
1533
1534 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1535 {
1536 #if DEBUG
1537   if (!IN_SCR_FIELD(x, y))
1538   {
1539     printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1540     printf("DrawGraphicThruMask(): This should never happen!\n");
1541     return;
1542   }
1543 #endif
1544
1545   DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1546                               graphic, frame);
1547   MarkTileDirty(x, y);
1548 }
1549
1550 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1551                             int frame)
1552 {
1553   Bitmap *src_bitmap;
1554   int src_x, src_y;
1555
1556   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1557
1558   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1559                    dst_x, dst_y);
1560 }
1561
1562 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1563                                  int graphic, int frame)
1564 {
1565   Bitmap *src_bitmap;
1566   int src_x, src_y;
1567
1568   getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1569
1570   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1571                    dst_x, dst_y);
1572 }
1573
1574 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1575 {
1576   DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1577                       frame, tilesize);
1578   MarkTileDirty(x / tilesize, y / tilesize);
1579 }
1580
1581 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1582                          int tilesize)
1583 {
1584   Bitmap *src_bitmap;
1585   int src_x, src_y;
1586
1587   getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1588   BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1589 }
1590
1591 void DrawMiniGraphic(int x, int y, int graphic)
1592 {
1593   DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1594   MarkTileDirty(x / 2, y / 2);
1595 }
1596
1597 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1598 {
1599   Bitmap *src_bitmap;
1600   int src_x, src_y;
1601
1602   getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1603   BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1604 }
1605
1606 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1607                                             int graphic, int frame,
1608                                             int cut_mode, int mask_mode)
1609 {
1610   Bitmap *src_bitmap;
1611   int src_x, src_y;
1612   int dst_x, dst_y;
1613   int width = TILEX, height = TILEY;
1614   int cx = 0, cy = 0;
1615
1616   if (dx || dy)                 /* shifted graphic */
1617   {
1618     if (x < BX1)                /* object enters playfield from the left */
1619     {
1620       x = BX1;
1621       width = dx;
1622       cx = TILEX - dx;
1623       dx = 0;
1624     }
1625     else if (x > BX2)           /* object enters playfield from the right */
1626     {
1627       x = BX2;
1628       width = -dx;
1629       dx = TILEX + dx;
1630     }
1631     else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1632     {
1633       width += dx;
1634       cx = -dx;
1635       dx = 0;
1636     }
1637     else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1638       width -= dx;
1639     else if (dx)                /* general horizontal movement */
1640       MarkTileDirty(x + SIGN(dx), y);
1641
1642     if (y < BY1)                /* object enters playfield from the top */
1643     {
1644       if (cut_mode == CUT_BELOW) /* object completely above top border */
1645         return;
1646
1647       y = BY1;
1648       height = dy;
1649       cy = TILEY - dy;
1650       dy = 0;
1651     }
1652     else if (y > BY2)           /* object enters playfield from the bottom */
1653     {
1654       y = BY2;
1655       height = -dy;
1656       dy = TILEY + dy;
1657     }
1658     else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1659     {
1660       height += dy;
1661       cy = -dy;
1662       dy = 0;
1663     }
1664     else if (dy > 0 && cut_mode == CUT_ABOVE)
1665     {
1666       if (y == BY2)             /* object completely above bottom border */
1667         return;
1668
1669       height = dy;
1670       cy = TILEY - dy;
1671       dy = TILEY;
1672       MarkTileDirty(x, y + 1);
1673     }                           /* object leaves playfield to the bottom */
1674     else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1675       height -= dy;
1676     else if (dy)                /* general vertical movement */
1677       MarkTileDirty(x, y + SIGN(dy));
1678   }
1679
1680 #if DEBUG
1681   if (!IN_SCR_FIELD(x, y))
1682   {
1683     printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1684     printf("DrawGraphicShifted(): This should never happen!\n");
1685     return;
1686   }
1687 #endif
1688
1689   width = width * TILESIZE_VAR / TILESIZE;
1690   height = height * TILESIZE_VAR / TILESIZE;
1691   cx = cx * TILESIZE_VAR / TILESIZE;
1692   cy = cy * TILESIZE_VAR / TILESIZE;
1693   dx = dx * TILESIZE_VAR / TILESIZE;
1694   dy = dy * TILESIZE_VAR / TILESIZE;
1695
1696   if (width > 0 && height > 0)
1697   {
1698     getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1699
1700     src_x += cx;
1701     src_y += cy;
1702
1703     dst_x = FX + x * TILEX_VAR + dx;
1704     dst_y = FY + y * TILEY_VAR + dy;
1705
1706     if (mask_mode == USE_MASKING)
1707       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1708                        dst_x, dst_y);
1709     else
1710       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1711                  dst_x, dst_y);
1712
1713     MarkTileDirty(x, y);
1714   }
1715 }
1716
1717 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1718                                             int graphic, int frame,
1719                                             int cut_mode, int mask_mode)
1720 {
1721   Bitmap *src_bitmap;
1722   int src_x, src_y;
1723   int dst_x, dst_y;
1724   int width = TILEX_VAR, height = TILEY_VAR;
1725   int x1 = x;
1726   int y1 = y;
1727   int x2 = x + SIGN(dx);
1728   int y2 = y + SIGN(dy);
1729
1730   /* movement with two-tile animations must be sync'ed with movement position,
1731      not with current GfxFrame (which can be higher when using slow movement) */
1732   int anim_pos = (dx ? ABS(dx) : ABS(dy));
1733   int anim_frames = graphic_info[graphic].anim_frames;
1734
1735   /* (we also need anim_delay here for movement animations with less frames) */
1736   int anim_delay = graphic_info[graphic].anim_delay;
1737   int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1738
1739   boolean draw_start_tile = (cut_mode != CUT_ABOVE);    /* only for falling! */
1740   boolean draw_end_tile   = (cut_mode != CUT_BELOW);    /* only for falling! */
1741
1742   /* re-calculate animation frame for two-tile movement animation */
1743   frame = getGraphicAnimationFrame(graphic, sync_frame);
1744
1745   /* check if movement start graphic inside screen area and should be drawn */
1746   if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1747   {
1748     getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1749
1750     dst_x = FX + x1 * TILEX_VAR;
1751     dst_y = FY + y1 * TILEY_VAR;
1752
1753     if (mask_mode == USE_MASKING)
1754       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1755                        dst_x, dst_y);
1756     else
1757       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1758                  dst_x, dst_y);
1759
1760     MarkTileDirty(x1, y1);
1761   }
1762
1763   /* check if movement end graphic inside screen area and should be drawn */
1764   if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1765   {
1766     getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1767
1768     dst_x = FX + x2 * TILEX_VAR;
1769     dst_y = FY + y2 * TILEY_VAR;
1770
1771     if (mask_mode == USE_MASKING)
1772       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1773                        dst_x, dst_y);
1774     else
1775       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1776                  dst_x, dst_y);
1777
1778     MarkTileDirty(x2, y2);
1779   }
1780 }
1781
1782 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1783                                int graphic, int frame,
1784                                int cut_mode, int mask_mode)
1785 {
1786   if (graphic < 0)
1787   {
1788     DrawGraphic(x, y, graphic, frame);
1789
1790     return;
1791   }
1792
1793   if (graphic_info[graphic].double_movement)    /* EM style movement images */
1794     DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1795   else
1796     DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1797 }
1798
1799 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1800                                 int frame, int cut_mode)
1801 {
1802   DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1803 }
1804
1805 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1806                           int cut_mode, int mask_mode)
1807 {
1808   int lx = LEVELX(x), ly = LEVELY(y);
1809   int graphic;
1810   int frame;
1811
1812   if (IN_LEV_FIELD(lx, ly))
1813   {
1814     SetRandomAnimationValue(lx, ly);
1815
1816     graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1817     frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1818
1819     /* do not use double (EM style) movement graphic when not moving */
1820     if (graphic_info[graphic].double_movement && !dx && !dy)
1821     {
1822       graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1823       frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1824     }
1825   }
1826   else  /* border element */
1827   {
1828     graphic = el2img(element);
1829     frame = getGraphicAnimationFrame(graphic, -1);
1830   }
1831
1832   if (element == EL_EXPANDABLE_WALL)
1833   {
1834     boolean left_stopped = FALSE, right_stopped = FALSE;
1835
1836     if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1837       left_stopped = TRUE;
1838     if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1839       right_stopped = TRUE;
1840
1841     if (left_stopped && right_stopped)
1842       graphic = IMG_WALL;
1843     else if (left_stopped)
1844     {
1845       graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1846       frame = graphic_info[graphic].anim_frames - 1;
1847     }
1848     else if (right_stopped)
1849     {
1850       graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1851       frame = graphic_info[graphic].anim_frames - 1;
1852     }
1853   }
1854
1855   if (dx || dy)
1856     DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1857   else if (mask_mode == USE_MASKING)
1858     DrawGraphicThruMask(x, y, graphic, frame);
1859   else
1860     DrawGraphic(x, y, graphic, frame);
1861 }
1862
1863 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1864                          int cut_mode, int mask_mode)
1865 {
1866   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1867     DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1868                          cut_mode, mask_mode);
1869 }
1870
1871 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1872                               int cut_mode)
1873 {
1874   DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1875 }
1876
1877 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1878                              int cut_mode)
1879 {
1880   DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1881 }
1882
1883 void DrawLevelElementThruMask(int x, int y, int element)
1884 {
1885   DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1886 }
1887
1888 void DrawLevelFieldThruMask(int x, int y)
1889 {
1890   DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1891 }
1892
1893 /* !!! implementation of quicksand is totally broken !!! */
1894 #define IS_CRUMBLED_TILE(x, y, e)                                       \
1895         (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) ||                     \
1896                              !IS_MOVING(x, y) ||                        \
1897                              (e) == EL_QUICKSAND_EMPTYING ||            \
1898                              (e) == EL_QUICKSAND_FAST_EMPTYING))
1899
1900 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1901                                                int graphic)
1902 {
1903   Bitmap *src_bitmap;
1904   int src_x, src_y;
1905   int width, height, cx, cy;
1906   int sx = SCREENX(x), sy = SCREENY(y);
1907   int crumbled_border_size = graphic_info[graphic].border_size;
1908   int crumbled_tile_size = graphic_info[graphic].tile_size;
1909   int crumbled_border_size_var =
1910     crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
1911   int i;
1912
1913   getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1914
1915   for (i = 1; i < 4; i++)
1916   {
1917     int dxx = (i & 1 ? dx : 0);
1918     int dyy = (i & 2 ? dy : 0);
1919     int xx = x + dxx;
1920     int yy = y + dyy;
1921     int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1922                    BorderElement);
1923
1924     /* check if neighbour field is of same crumble type */
1925     boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1926                     graphic_info[graphic].class ==
1927                     graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1928
1929     /* return if check prevents inner corner */
1930     if (same == (dxx == dx && dyy == dy))
1931       return;
1932   }
1933
1934   /* if we reach this point, we have an inner corner */
1935
1936   getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1937
1938   width  = crumbled_border_size_var;
1939   height = crumbled_border_size_var;
1940   cx = (dx > 0 ? TILESIZE_VAR - width  : 0);
1941   cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1942
1943   BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1944              width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1945 }
1946
1947 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1948                                           int dir)
1949 {
1950   Bitmap *src_bitmap;
1951   int src_x, src_y;
1952   int width, height, bx, by, cx, cy;
1953   int sx = SCREENX(x), sy = SCREENY(y);
1954   int crumbled_border_size = graphic_info[graphic].border_size;
1955   int crumbled_tile_size = graphic_info[graphic].tile_size;
1956   int crumbled_border_size_var =
1957     crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
1958   int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1959   int i;
1960
1961   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1962
1963   /* draw simple, sloppy, non-corner-accurate crumbled border */
1964
1965   width  = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1966   height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1967   cx = (dir == 2 ? crumbled_border_pos_var : 0);
1968   cy = (dir == 3 ? crumbled_border_pos_var : 0);
1969
1970   BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1971              FX + sx * TILEX_VAR + cx,
1972              FY + sy * TILEY_VAR + cy);
1973
1974   /* (remaining middle border part must be at least as big as corner part) */
1975   if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1976       crumbled_border_size_var >= TILESIZE_VAR / 3)
1977     return;
1978
1979   /* correct corners of crumbled border, if needed */
1980
1981   for (i = -1; i <= 1; i += 2)
1982   {
1983     int xx = x + (dir == 0 || dir == 3 ? i : 0);
1984     int yy = y + (dir == 1 || dir == 2 ? i : 0);
1985     int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1986                    BorderElement);
1987
1988     /* check if neighbour field is of same crumble type */
1989     if (IS_CRUMBLED_TILE(xx, yy, element) &&
1990         graphic_info[graphic].class ==
1991         graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1992     {
1993       /* no crumbled corner, but continued crumbled border */
1994
1995       int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1996       int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1997       int b1 = (i == 1 ? crumbled_border_size_var :
1998                 TILESIZE_VAR - 2 * crumbled_border_size_var);
1999
2000       width  = crumbled_border_size_var;
2001       height = crumbled_border_size_var;
2002
2003       if (dir == 1 || dir == 2)
2004       {
2005         cx = c1;
2006         cy = c2;
2007         bx = cx;
2008         by = b1;
2009       }
2010       else
2011       {
2012         cx = c2;
2013         cy = c1;
2014         bx = b1;
2015         by = cy;
2016       }
2017
2018       BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2019                  width, height,
2020                  FX + sx * TILEX_VAR + cx,
2021                  FY + sy * TILEY_VAR + cy);
2022     }
2023   }
2024 }
2025
2026 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2027 {
2028   int sx = SCREENX(x), sy = SCREENY(y);
2029   int element;
2030   int i;
2031   static int xy[4][2] =
2032   {
2033     { 0, -1 },
2034     { -1, 0 },
2035     { +1, 0 },
2036     { 0, +1 }
2037   };
2038
2039   if (!IN_LEV_FIELD(x, y))
2040     return;
2041
2042   element = TILE_GFX_ELEMENT(x, y);
2043
2044   if (IS_CRUMBLED_TILE(x, y, element))          /* crumble field itself */
2045   {
2046     if (!IN_SCR_FIELD(sx, sy))
2047       return;
2048
2049     /* crumble field borders towards direct neighbour fields */
2050     for (i = 0; i < 4; i++)
2051     {
2052       int xx = x + xy[i][0];
2053       int yy = y + xy[i][1];
2054
2055       element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2056                  BorderElement);
2057
2058       /* check if neighbour field is of same crumble type */
2059       if (IS_CRUMBLED_TILE(xx, yy, element) &&
2060           graphic_info[graphic].class ==
2061           graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2062         continue;
2063
2064       DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2065     }
2066
2067     /* crumble inner field corners towards corner neighbour fields */
2068     if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2069         graphic_info[graphic].anim_frames == 2)
2070     {
2071       for (i = 0; i < 4; i++)
2072       {
2073         int dx = (i & 1 ? +1 : -1);
2074         int dy = (i & 2 ? +1 : -1);
2075
2076         DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2077       }
2078     }
2079
2080     MarkTileDirty(sx, sy);
2081   }
2082   else          /* center field is not crumbled -- crumble neighbour fields */
2083   {
2084     /* crumble field borders of direct neighbour fields */
2085     for (i = 0; i < 4; i++)
2086     {
2087       int xx = x + xy[i][0];
2088       int yy = y + xy[i][1];
2089       int sxx = sx + xy[i][0];
2090       int syy = sy + xy[i][1];
2091
2092       if (!IN_LEV_FIELD(xx, yy) ||
2093           !IN_SCR_FIELD(sxx, syy))
2094         continue;
2095
2096       if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2097         continue;
2098
2099       element = TILE_GFX_ELEMENT(xx, yy);
2100
2101       if (!IS_CRUMBLED_TILE(xx, yy, element))
2102         continue;
2103
2104       graphic = el_act2crm(element, ACTION_DEFAULT);
2105
2106       DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2107
2108       MarkTileDirty(sxx, syy);
2109     }
2110
2111     /* crumble inner field corners of corner neighbour fields */
2112     for (i = 0; i < 4; i++)
2113     {
2114       int dx = (i & 1 ? +1 : -1);
2115       int dy = (i & 2 ? +1 : -1);
2116       int xx = x + dx;
2117       int yy = y + dy;
2118       int sxx = sx + dx;
2119       int syy = sy + dy;
2120
2121       if (!IN_LEV_FIELD(xx, yy) ||
2122           !IN_SCR_FIELD(sxx, syy))
2123         continue;
2124
2125       if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2126         continue;
2127
2128       element = TILE_GFX_ELEMENT(xx, yy);
2129
2130       if (!IS_CRUMBLED_TILE(xx, yy, element))
2131         continue;
2132
2133       graphic = el_act2crm(element, ACTION_DEFAULT);
2134
2135       if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2136           graphic_info[graphic].anim_frames == 2)
2137         DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2138
2139       MarkTileDirty(sxx, syy);
2140     }
2141   }
2142 }
2143
2144 void DrawLevelFieldCrumbled(int x, int y)
2145 {
2146   int graphic;
2147
2148   if (!IN_LEV_FIELD(x, y))
2149     return;
2150
2151   if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2152       GfxElement[x][y] != EL_UNDEFINED &&
2153       GFX_CRUMBLED(GfxElement[x][y]))
2154   {
2155     DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2156
2157     return;
2158   }
2159
2160   graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2161
2162   DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2163 }
2164
2165 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2166                                    int step_frame)
2167 {
2168   int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2169   int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2170   int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2171   int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2172   int sx = SCREENX(x), sy = SCREENY(y);
2173
2174   DrawGraphic(sx, sy, graphic1, frame1);
2175   DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2176 }
2177
2178 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2179 {
2180   int sx = SCREENX(x), sy = SCREENY(y);
2181   static int xy[4][2] =
2182   {
2183     { 0, -1 },
2184     { -1, 0 },
2185     { +1, 0 },
2186     { 0, +1 }
2187   };
2188   int i;
2189
2190   /* crumble direct neighbour fields (required for field borders) */
2191   for (i = 0; i < 4; i++)
2192   {
2193     int xx = x + xy[i][0];
2194     int yy = y + xy[i][1];
2195     int sxx = sx + xy[i][0];
2196     int syy = sy + xy[i][1];
2197
2198     if (!IN_LEV_FIELD(xx, yy) ||
2199         !IN_SCR_FIELD(sxx, syy) ||
2200         !GFX_CRUMBLED(Feld[xx][yy]) ||
2201         IS_MOVING(xx, yy))
2202       continue;
2203
2204     DrawLevelField(xx, yy);
2205   }
2206
2207   /* crumble corner neighbour fields (required for inner field corners) */
2208   for (i = 0; i < 4; i++)
2209   {
2210     int dx = (i & 1 ? +1 : -1);
2211     int dy = (i & 2 ? +1 : -1);
2212     int xx = x + dx;
2213     int yy = y + dy;
2214     int sxx = sx + dx;
2215     int syy = sy + dy;
2216
2217     if (!IN_LEV_FIELD(xx, yy) ||
2218         !IN_SCR_FIELD(sxx, syy) ||
2219         !GFX_CRUMBLED(Feld[xx][yy]) ||
2220         IS_MOVING(xx, yy))
2221       continue;
2222
2223     int element = TILE_GFX_ELEMENT(xx, yy);
2224     int graphic = el_act2crm(element, ACTION_DEFAULT);
2225
2226     if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2227         graphic_info[graphic].anim_frames == 2)
2228       DrawLevelField(xx, yy);
2229   }
2230 }
2231
2232 static int getBorderElement(int x, int y)
2233 {
2234   int border[7][2] =
2235   {
2236     { EL_STEELWALL_TOPLEFT,             EL_INVISIBLE_STEELWALL_TOPLEFT     },
2237     { EL_STEELWALL_TOPRIGHT,            EL_INVISIBLE_STEELWALL_TOPRIGHT    },
2238     { EL_STEELWALL_BOTTOMLEFT,          EL_INVISIBLE_STEELWALL_BOTTOMLEFT  },
2239     { EL_STEELWALL_BOTTOMRIGHT,         EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2240     { EL_STEELWALL_VERTICAL,            EL_INVISIBLE_STEELWALL_VERTICAL    },
2241     { EL_STEELWALL_HORIZONTAL,          EL_INVISIBLE_STEELWALL_HORIZONTAL  },
2242     { EL_STEELWALL,                     EL_INVISIBLE_STEELWALL             }
2243   };
2244   int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2245   int steel_position = (x == -1         && y == -1              ? 0 :
2246                         x == lev_fieldx && y == -1              ? 1 :
2247                         x == -1         && y == lev_fieldy      ? 2 :
2248                         x == lev_fieldx && y == lev_fieldy      ? 3 :
2249                         x == -1         || x == lev_fieldx      ? 4 :
2250                         y == -1         || y == lev_fieldy      ? 5 : 6);
2251
2252   return border[steel_position][steel_type];
2253 }
2254
2255 void DrawScreenElement(int x, int y, int element)
2256 {
2257   DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2258   DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2259 }
2260
2261 void DrawLevelElement(int x, int y, int element)
2262 {
2263   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2264     DrawScreenElement(SCREENX(x), SCREENY(y), element);
2265 }
2266
2267 void DrawScreenField(int x, int y)
2268 {
2269   int lx = LEVELX(x), ly = LEVELY(y);
2270   int element, content;
2271
2272   if (!IN_LEV_FIELD(lx, ly))
2273   {
2274     if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2275       element = EL_EMPTY;
2276     else
2277       element = getBorderElement(lx, ly);
2278
2279     DrawScreenElement(x, y, element);
2280
2281     return;
2282   }
2283
2284   element = Feld[lx][ly];
2285   content = Store[lx][ly];
2286
2287   if (IS_MOVING(lx, ly))
2288   {
2289     int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2290     boolean cut_mode = NO_CUTTING;
2291
2292     if (element == EL_QUICKSAND_EMPTYING ||
2293         element == EL_QUICKSAND_FAST_EMPTYING ||
2294         element == EL_MAGIC_WALL_EMPTYING ||
2295         element == EL_BD_MAGIC_WALL_EMPTYING ||
2296         element == EL_DC_MAGIC_WALL_EMPTYING ||
2297         element == EL_AMOEBA_DROPPING)
2298       cut_mode = CUT_ABOVE;
2299     else if (element == EL_QUICKSAND_FILLING ||
2300              element == EL_QUICKSAND_FAST_FILLING ||
2301              element == EL_MAGIC_WALL_FILLING ||
2302              element == EL_BD_MAGIC_WALL_FILLING ||
2303              element == EL_DC_MAGIC_WALL_FILLING)
2304       cut_mode = CUT_BELOW;
2305
2306     if (cut_mode == CUT_ABOVE)
2307       DrawScreenElement(x, y, element);
2308     else
2309       DrawScreenElement(x, y, EL_EMPTY);
2310
2311     if (horiz_move)
2312       DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2313     else if (cut_mode == NO_CUTTING)
2314       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2315     else
2316     {
2317       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2318
2319       if (cut_mode == CUT_BELOW &&
2320           IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2321         DrawLevelElement(lx, ly + 1, element);
2322     }
2323
2324     if (content == EL_ACID)
2325     {
2326       int dir = MovDir[lx][ly];
2327       int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2328       int newly = ly + (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
2329
2330       DrawLevelElementThruMask(newlx, newly, EL_ACID);
2331
2332       // prevent target field from being drawn again (but without masking)
2333       // (this would happen if target field is scanned after moving element)
2334       Stop[newlx][newly] = TRUE;
2335     }
2336   }
2337   else if (IS_BLOCKED(lx, ly))
2338   {
2339     int oldx, oldy;
2340     int sx, sy;
2341     int horiz_move;
2342     boolean cut_mode = NO_CUTTING;
2343     int element_old, content_old;
2344
2345     Blocked2Moving(lx, ly, &oldx, &oldy);
2346     sx = SCREENX(oldx);
2347     sy = SCREENY(oldy);
2348     horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2349                   MovDir[oldx][oldy] == MV_RIGHT);
2350
2351     element_old = Feld[oldx][oldy];
2352     content_old = Store[oldx][oldy];
2353
2354     if (element_old == EL_QUICKSAND_EMPTYING ||
2355         element_old == EL_QUICKSAND_FAST_EMPTYING ||
2356         element_old == EL_MAGIC_WALL_EMPTYING ||
2357         element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2358         element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2359         element_old == EL_AMOEBA_DROPPING)
2360       cut_mode = CUT_ABOVE;
2361
2362     DrawScreenElement(x, y, EL_EMPTY);
2363
2364     if (horiz_move)
2365       DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2366                                NO_CUTTING);
2367     else if (cut_mode == NO_CUTTING)
2368       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2369                                cut_mode);
2370     else
2371       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2372                                cut_mode);
2373   }
2374   else if (IS_DRAWABLE(element))
2375     DrawScreenElement(x, y, element);
2376   else
2377     DrawScreenElement(x, y, EL_EMPTY);
2378 }
2379
2380 void DrawLevelField(int x, int y)
2381 {
2382   if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2383     DrawScreenField(SCREENX(x), SCREENY(y));
2384   else if (IS_MOVING(x, y))
2385   {
2386     int newx,newy;
2387
2388     Moving2Blocked(x, y, &newx, &newy);
2389     if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2390       DrawScreenField(SCREENX(newx), SCREENY(newy));
2391   }
2392   else if (IS_BLOCKED(x, y))
2393   {
2394     int oldx, oldy;
2395
2396     Blocked2Moving(x, y, &oldx, &oldy);
2397     if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2398       DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2399   }
2400 }
2401
2402 void DrawSizedElement(int x, int y, int element, int tilesize)
2403 {
2404   int graphic;
2405
2406   graphic = el2edimg(element);
2407   DrawSizedGraphic(x, y, graphic, 0, tilesize);
2408 }
2409
2410 void DrawMiniElement(int x, int y, int element)
2411 {
2412   int graphic;
2413
2414   graphic = el2edimg(element);
2415   DrawMiniGraphic(x, y, graphic);
2416 }
2417
2418 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2419                             int tilesize)
2420 {
2421   int x = sx + scroll_x, y = sy + scroll_y;
2422
2423   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2424     DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2425   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2426     DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2427   else
2428     DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2429 }
2430
2431 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2432 {
2433   int x = sx + scroll_x, y = sy + scroll_y;
2434
2435   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2436     DrawMiniElement(sx, sy, EL_EMPTY);
2437   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2438     DrawMiniElement(sx, sy, Feld[x][y]);
2439   else
2440     DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2441 }
2442
2443 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2444                                  int x, int y, int xsize, int ysize,
2445                                  int tile_width, int tile_height)
2446 {
2447   Bitmap *src_bitmap;
2448   int src_x, src_y;
2449   int dst_x = startx + x * tile_width;
2450   int dst_y = starty + y * tile_height;
2451   int width  = graphic_info[graphic].width;
2452   int height = graphic_info[graphic].height;
2453   int inner_width_raw  = MAX(width  - 2 * tile_width,  tile_width);
2454   int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2455   int inner_width  = inner_width_raw  - (inner_width_raw  % tile_width);
2456   int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2457   int inner_sx = (width  >= 3 * tile_width  ? tile_width  : 0);
2458   int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2459   boolean draw_masked = graphic_info[graphic].draw_masked;
2460
2461   getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2462
2463   if (src_bitmap == NULL || width < tile_width || height < tile_height)
2464   {
2465     ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2466     return;
2467   }
2468
2469   src_x += (x == 0 ? 0 : x == xsize - 1 ? width  - tile_width  :
2470             inner_sx + (x - 1) * tile_width  % inner_width);
2471   src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2472             inner_sy + (y - 1) * tile_height % inner_height);
2473
2474   if (draw_masked)
2475     BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2476                      dst_x, dst_y);
2477   else
2478     BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2479                dst_x, dst_y);
2480 }
2481
2482 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2483                             int x, int y, int xsize, int ysize, int font_nr)
2484 {
2485   int font_width  = getFontWidth(font_nr);
2486   int font_height = getFontHeight(font_nr);
2487
2488   DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2489                               font_width, font_height);
2490 }
2491
2492 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2493 {
2494   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2495   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2496   int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2497   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2498   boolean no_delay = (tape.warp_forward);
2499   unsigned int anim_delay = 0;
2500   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2501   int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2502   int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2503   int font_width = getFontWidth(font_nr);
2504   int font_height = getFontHeight(font_nr);
2505   int max_xsize = level.envelope[envelope_nr].xsize;
2506   int max_ysize = level.envelope[envelope_nr].ysize;
2507   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2508   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2509   int xend = max_xsize;
2510   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2511   int xstep = (xstart < xend ? 1 : 0);
2512   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2513   int start = 0;
2514   int end = MAX(xend - xstart, yend - ystart);
2515   int i;
2516
2517   for (i = start; i <= end; i++)
2518   {
2519     int last_frame = end;       // last frame of this "for" loop
2520     int x = xstart + i * xstep;
2521     int y = ystart + i * ystep;
2522     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2523     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2524     int sx = SX + (SXSIZE - xsize * font_width)  / 2;
2525     int sy = SY + (SYSIZE - ysize * font_height) / 2;
2526     int xx, yy;
2527
2528     SetDrawtoField(DRAW_TO_FIELDBUFFER);
2529
2530     BlitScreenToBitmap(backbuffer);
2531
2532     SetDrawtoField(DRAW_TO_BACKBUFFER);
2533
2534     for (yy = 0; yy < ysize; yy++)
2535       for (xx = 0; xx < xsize; xx++)
2536         DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2537
2538     DrawTextBuffer(sx + font_width, sy + font_height,
2539                    level.envelope[envelope_nr].text, font_nr, max_xsize,
2540                    xsize - 2, ysize - 2, 0, mask_mode,
2541                    level.envelope[envelope_nr].autowrap,
2542                    level.envelope[envelope_nr].centered, FALSE);
2543
2544     redraw_mask |= REDRAW_FIELD;
2545     BackToFront();
2546
2547     SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2548   }
2549 }
2550
2551 void ShowEnvelope(int envelope_nr)
2552 {
2553   int element = EL_ENVELOPE_1 + envelope_nr;
2554   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2555   int sound_opening = element_info[element].sound[ACTION_OPENING];
2556   int sound_closing = element_info[element].sound[ACTION_CLOSING];
2557   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2558   boolean no_delay = (tape.warp_forward);
2559   int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2560   int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2561   int anim_mode = graphic_info[graphic].anim_mode;
2562   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2563                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2564
2565   game.envelope_active = TRUE;  /* needed for RedrawPlayfield() events */
2566
2567   PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2568
2569   if (anim_mode == ANIM_DEFAULT)
2570     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2571
2572   AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2573
2574   if (tape.playing)
2575     Delay(wait_delay_value);
2576   else
2577     WaitForEventToContinue();
2578
2579   PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2580
2581   if (anim_mode != ANIM_NONE)
2582     AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2583
2584   if (anim_mode == ANIM_DEFAULT)
2585     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2586
2587   game.envelope_active = FALSE;
2588
2589   SetDrawtoField(DRAW_TO_FIELDBUFFER);
2590
2591   redraw_mask |= REDRAW_FIELD;
2592   BackToFront();
2593 }
2594
2595 static void setRequestBasePosition(int *x, int *y)
2596 {
2597   int sx_base, sy_base;
2598
2599   if (request.x != -1)
2600     sx_base = request.x;
2601   else if (request.align == ALIGN_LEFT)
2602     sx_base = SX;
2603   else if (request.align == ALIGN_RIGHT)
2604     sx_base = SX + SXSIZE;
2605   else
2606     sx_base = SX + SXSIZE / 2;
2607
2608   if (request.y != -1)
2609     sy_base = request.y;
2610   else if (request.valign == VALIGN_TOP)
2611     sy_base = SY;
2612   else if (request.valign == VALIGN_BOTTOM)
2613     sy_base = SY + SYSIZE;
2614   else
2615     sy_base = SY + SYSIZE / 2;
2616
2617   *x = sx_base;
2618   *y = sy_base;
2619 }
2620
2621 static void setRequestPositionExt(int *x, int *y, int width, int height,
2622                                   boolean add_border_size)
2623 {
2624   int border_size = request.border_size;
2625   int sx_base, sy_base;
2626   int sx, sy;
2627
2628   setRequestBasePosition(&sx_base, &sy_base);
2629
2630   if (request.align == ALIGN_LEFT)
2631     sx = sx_base;
2632   else if (request.align == ALIGN_RIGHT)
2633     sx = sx_base - width;
2634   else
2635     sx = sx_base - width  / 2;
2636
2637   if (request.valign == VALIGN_TOP)
2638     sy = sy_base;
2639   else if (request.valign == VALIGN_BOTTOM)
2640     sy = sy_base - height;
2641   else
2642     sy = sy_base - height / 2;
2643
2644   sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2645   sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2646
2647   if (add_border_size)
2648   {
2649     sx += border_size;
2650     sy += border_size;
2651   }
2652
2653   *x = sx;
2654   *y = sy;
2655 }
2656
2657 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2658 {
2659   setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2660 }
2661
2662 void DrawEnvelopeRequest(char *text)
2663 {
2664   char *text_final = text;
2665   char *text_door_style = NULL;
2666   int graphic = IMG_BACKGROUND_REQUEST;
2667   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2668   int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2669   int font_nr = FONT_REQUEST;
2670   int font_width = getFontWidth(font_nr);
2671   int font_height = getFontHeight(font_nr);
2672   int border_size = request.border_size;
2673   int line_spacing = request.line_spacing;
2674   int line_height = font_height + line_spacing;
2675   int max_text_width  = request.width  - 2 * border_size;
2676   int max_text_height = request.height - 2 * border_size;
2677   int line_length = max_text_width  / font_width;
2678   int max_lines   = max_text_height / line_height;
2679   int text_width = line_length * font_width;
2680   int width = request.width;
2681   int height = request.height;
2682   int tile_size = MAX(request.step_offset, 1);
2683   int x_steps = width  / tile_size;
2684   int y_steps = height / tile_size;
2685   int sx_offset = border_size;
2686   int sy_offset = border_size;
2687   int sx, sy;
2688   int i, x, y;
2689
2690   if (request.centered)
2691     sx_offset = (request.width - text_width) / 2;
2692
2693   if (request.wrap_single_words && !request.autowrap)
2694   {
2695     char *src_text_ptr, *dst_text_ptr;
2696
2697     text_door_style = checked_malloc(2 * strlen(text) + 1);
2698
2699     src_text_ptr = text;
2700     dst_text_ptr = text_door_style;
2701
2702     while (*src_text_ptr)
2703     {
2704       if (*src_text_ptr == ' ' ||
2705           *src_text_ptr == '?' ||
2706           *src_text_ptr == '!')
2707         *dst_text_ptr++ = '\n';
2708
2709       if (*src_text_ptr != ' ')
2710         *dst_text_ptr++ = *src_text_ptr;
2711
2712       src_text_ptr++;
2713     }
2714
2715     *dst_text_ptr = '\0';
2716
2717     text_final = text_door_style;
2718   }
2719
2720   setRequestPosition(&sx, &sy, FALSE);
2721
2722   ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2723
2724   for (y = 0; y < y_steps; y++)
2725     for (x = 0; x < x_steps; x++)
2726       DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2727                                   x, y, x_steps, y_steps,
2728                                   tile_size, tile_size);
2729
2730   /* force DOOR font inside door area */
2731   SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2732
2733   DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2734                  line_length, -1, max_lines, line_spacing, mask_mode,
2735                  request.autowrap, request.centered, FALSE);
2736
2737   ResetFontStatus();
2738
2739   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2740     RedrawGadget(tool_gadget[i]);
2741
2742   // store readily prepared envelope request for later use when animating
2743   BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2744
2745   if (text_door_style)
2746     free(text_door_style);
2747 }
2748
2749 void AnimateEnvelopeRequest(int anim_mode, int action)
2750 {
2751   int graphic = IMG_BACKGROUND_REQUEST;
2752   boolean draw_masked = graphic_info[graphic].draw_masked;
2753   int delay_value_normal = request.step_delay;
2754   int delay_value_fast = delay_value_normal / 2;
2755   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2756   boolean no_delay = (tape.warp_forward);
2757   int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2758   int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2759   unsigned int anim_delay = 0;
2760
2761   int tile_size = MAX(request.step_offset, 1);
2762   int max_xsize = request.width  / tile_size;
2763   int max_ysize = request.height / tile_size;
2764   int max_xsize_inner = max_xsize - 2;
2765   int max_ysize_inner = max_ysize - 2;
2766
2767   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2768   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2769   int xend = max_xsize_inner;
2770   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2771   int xstep = (xstart < xend ? 1 : 0);
2772   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2773   int start = 0;
2774   int end = MAX(xend - xstart, yend - ystart);
2775   int i;
2776
2777   if (setup.quick_doors)
2778   {
2779     xstart = xend;
2780     ystart = yend;
2781     end = 0;
2782   }
2783
2784   for (i = start; i <= end; i++)
2785   {
2786     int last_frame = end;       // last frame of this "for" loop
2787     int x = xstart + i * xstep;
2788     int y = ystart + i * ystep;
2789     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2790     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2791     int xsize_size_left = (xsize - 1) * tile_size;
2792     int ysize_size_top  = (ysize - 1) * tile_size;
2793     int max_xsize_pos = (max_xsize - 1) * tile_size;
2794     int max_ysize_pos = (max_ysize - 1) * tile_size;
2795     int width  = xsize * tile_size;
2796     int height = ysize * tile_size;
2797     int src_x, src_y;
2798     int dst_x, dst_y;
2799     int xx, yy;
2800
2801     setRequestPosition(&src_x, &src_y, FALSE);
2802     setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2803
2804     BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2805
2806     for (yy = 0; yy < 2; yy++)
2807     {
2808       for (xx = 0; xx < 2; xx++)
2809       {
2810         int src_xx = src_x + xx * max_xsize_pos;
2811         int src_yy = src_y + yy * max_ysize_pos;
2812         int dst_xx = dst_x + xx * xsize_size_left;
2813         int dst_yy = dst_y + yy * ysize_size_top;
2814         int xx_size = (xx ? tile_size : xsize_size_left);
2815         int yy_size = (yy ? tile_size : ysize_size_top);
2816
2817         if (draw_masked)
2818           BlitBitmapMasked(bitmap_db_store_2, backbuffer,
2819                            src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2820         else
2821           BlitBitmap(bitmap_db_store_2, backbuffer,
2822                      src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2823       }
2824     }
2825
2826     redraw_mask |= REDRAW_FIELD;
2827
2828     BackToFront();
2829
2830     SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2831   }
2832 }
2833
2834 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2835 {
2836   int graphic = IMG_BACKGROUND_REQUEST;
2837   int sound_opening = SND_REQUEST_OPENING;
2838   int sound_closing = SND_REQUEST_CLOSING;
2839   int anim_mode_1 = request.anim_mode;                  /* (higher priority) */
2840   int anim_mode_2 = graphic_info[graphic].anim_mode;    /* (lower priority) */
2841   int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2842   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2843                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2844
2845   if (game_status == GAME_MODE_PLAYING)
2846     BlitScreenToBitmap(backbuffer);
2847
2848   SetDrawtoField(DRAW_TO_BACKBUFFER);
2849
2850   // SetDrawBackgroundMask(REDRAW_NONE);
2851
2852   if (action == ACTION_OPENING)
2853   {
2854     BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2855
2856     if (req_state & REQ_ASK)
2857     {
2858       MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2859       MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2860     }
2861     else if (req_state & REQ_CONFIRM)
2862     {
2863       MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2864     }
2865     else if (req_state & REQ_PLAYER)
2866     {
2867       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2868       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2869       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2870       MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2871     }
2872
2873     DrawEnvelopeRequest(text);
2874   }
2875
2876   game.envelope_active = TRUE;  /* needed for RedrawPlayfield() events */
2877
2878   if (action == ACTION_OPENING)
2879   {
2880     PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2881
2882     if (anim_mode == ANIM_DEFAULT)
2883       AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2884
2885     AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2886   }
2887   else
2888   {
2889     PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2890
2891     if (anim_mode != ANIM_NONE)
2892       AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2893
2894     if (anim_mode == ANIM_DEFAULT)
2895       AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2896   }
2897
2898   game.envelope_active = FALSE;
2899
2900   if (action == ACTION_CLOSING)
2901     BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2902
2903   // SetDrawBackgroundMask(last_draw_background_mask);
2904
2905   redraw_mask |= REDRAW_FIELD;
2906
2907   BackToFront();
2908
2909   if (action == ACTION_CLOSING &&
2910       game_status == GAME_MODE_PLAYING &&
2911       level.game_engine_type == GAME_ENGINE_TYPE_RND)
2912     SetDrawtoField(DRAW_TO_FIELDBUFFER);
2913 }
2914
2915 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2916 {
2917   Bitmap *src_bitmap;
2918   int src_x, src_y;
2919   int graphic = el2preimg(element);
2920
2921   getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2922   BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2923 }
2924
2925 void DrawLevel(int draw_background_mask)
2926 {
2927   int x,y;
2928
2929   SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2930   SetDrawBackgroundMask(draw_background_mask);
2931
2932   ClearField();
2933
2934   for (x = BX1; x <= BX2; x++)
2935     for (y = BY1; y <= BY2; y++)
2936       DrawScreenField(x, y);
2937
2938   redraw_mask |= REDRAW_FIELD;
2939 }
2940
2941 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2942                     int tilesize)
2943 {
2944   int x,y;
2945
2946   for (x = 0; x < size_x; x++)
2947     for (y = 0; y < size_y; y++)
2948       DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2949
2950   redraw_mask |= REDRAW_FIELD;
2951 }
2952
2953 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2954 {
2955   int x,y;
2956
2957   for (x = 0; x < size_x; x++)
2958     for (y = 0; y < size_y; y++)
2959       DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2960
2961   redraw_mask |= REDRAW_FIELD;
2962 }
2963
2964 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2965 {
2966   boolean show_level_border = (BorderElement != EL_EMPTY);
2967   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2968   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2969   int tile_size = preview.tile_size;
2970   int preview_width  = preview.xsize * tile_size;
2971   int preview_height = preview.ysize * tile_size;
2972   int real_preview_xsize = MIN(level_xsize, preview.xsize);
2973   int real_preview_ysize = MIN(level_ysize, preview.ysize);
2974   int real_preview_width  = real_preview_xsize * tile_size;
2975   int real_preview_height = real_preview_ysize * tile_size;
2976   int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2977   int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2978   int x, y;
2979
2980   if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2981     return;
2982
2983   DrawBackground(dst_x, dst_y, preview_width, preview_height);
2984
2985   dst_x += (preview_width  - real_preview_width)  / 2;
2986   dst_y += (preview_height - real_preview_height) / 2;
2987
2988   for (x = 0; x < real_preview_xsize; x++)
2989   {
2990     for (y = 0; y < real_preview_ysize; y++)
2991     {
2992       int lx = from_x + x + (show_level_border ? -1 : 0);
2993       int ly = from_y + y + (show_level_border ? -1 : 0);
2994       int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2995                      getBorderElement(lx, ly));
2996
2997       DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2998                          element, tile_size);
2999     }
3000   }
3001
3002   redraw_mask |= REDRAW_FIELD;
3003 }
3004
3005 #define MICROLABEL_EMPTY                0
3006 #define MICROLABEL_LEVEL_NAME           1
3007 #define MICROLABEL_LEVEL_AUTHOR_HEAD    2
3008 #define MICROLABEL_LEVEL_AUTHOR         3
3009 #define MICROLABEL_IMPORTED_FROM_HEAD   4
3010 #define MICROLABEL_IMPORTED_FROM        5
3011 #define MICROLABEL_IMPORTED_BY_HEAD     6
3012 #define MICROLABEL_IMPORTED_BY          7
3013
3014 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3015 {
3016   int max_text_width = SXSIZE;
3017   int font_width = getFontWidth(font_nr);
3018
3019   if (pos->align == ALIGN_CENTER)
3020     max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3021   else if (pos->align == ALIGN_RIGHT)
3022     max_text_width = pos->x;
3023   else
3024     max_text_width = SXSIZE - pos->x;
3025
3026   return max_text_width / font_width;
3027 }
3028
3029 static void DrawPreviewLevelLabelExt(int mode)
3030 {
3031   struct TextPosInfo *pos = &menu.main.text.level_info_2;
3032   char label_text[MAX_OUTPUT_LINESIZE + 1];
3033   int max_len_label_text;
3034   int font_nr = pos->font;
3035   int i;
3036
3037   if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3038     return;
3039
3040   if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3041       mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3042       mode == MICROLABEL_IMPORTED_BY_HEAD)
3043     font_nr = pos->font_alt;
3044
3045   max_len_label_text = getMaxTextLength(pos, font_nr);
3046
3047   if (pos->size != -1)
3048     max_len_label_text = pos->size;
3049
3050   for (i = 0; i < max_len_label_text; i++)
3051     label_text[i] = ' ';
3052   label_text[max_len_label_text] = '\0';
3053
3054   if (strlen(label_text) > 0)
3055     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3056
3057   strncpy(label_text,
3058           (mode == MICROLABEL_LEVEL_NAME ? level.name :
3059            mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3060            mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3061            mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3062            mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3063            mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3064            mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3065           max_len_label_text);
3066   label_text[max_len_label_text] = '\0';
3067
3068   if (strlen(label_text) > 0)
3069     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3070
3071   redraw_mask |= REDRAW_FIELD;
3072 }
3073
3074 static void DrawPreviewLevelExt(boolean restart)
3075 {
3076   static unsigned int scroll_delay = 0;
3077   static unsigned int label_delay = 0;
3078   static int from_x, from_y, scroll_direction;
3079   static int label_state, label_counter;
3080   unsigned int scroll_delay_value = preview.step_delay;
3081   boolean show_level_border = (BorderElement != EL_EMPTY);
3082   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3083   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3084
3085   if (restart)
3086   {
3087     from_x = 0;
3088     from_y = 0;
3089
3090     if (preview.anim_mode == ANIM_CENTERED)
3091     {
3092       if (level_xsize > preview.xsize)
3093         from_x = (level_xsize - preview.xsize) / 2;
3094       if (level_ysize > preview.ysize)
3095         from_y = (level_ysize - preview.ysize) / 2;
3096     }
3097
3098     from_x += preview.xoffset;
3099     from_y += preview.yoffset;
3100
3101     scroll_direction = MV_RIGHT;
3102     label_state = 1;
3103     label_counter = 0;
3104
3105     DrawPreviewLevelPlayfieldExt(from_x, from_y);
3106     DrawPreviewLevelLabelExt(label_state);
3107
3108     /* initialize delay counters */
3109     DelayReached(&scroll_delay, 0);
3110     DelayReached(&label_delay, 0);
3111
3112     if (leveldir_current->name)
3113     {
3114       struct TextPosInfo *pos = &menu.main.text.level_info_1;
3115       char label_text[MAX_OUTPUT_LINESIZE + 1];
3116       int font_nr = pos->font;
3117       int max_len_label_text = getMaxTextLength(pos, font_nr);
3118
3119       if (pos->size != -1)
3120         max_len_label_text = pos->size;
3121
3122       strncpy(label_text, leveldir_current->name, max_len_label_text);
3123       label_text[max_len_label_text] = '\0';
3124
3125       if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3126         DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3127     }
3128
3129     return;
3130   }
3131
3132   /* scroll preview level, if needed */
3133   if (preview.anim_mode != ANIM_NONE &&
3134       (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3135       DelayReached(&scroll_delay, scroll_delay_value))
3136   {
3137     switch (scroll_direction)
3138     {
3139       case MV_LEFT:
3140         if (from_x > 0)
3141         {
3142           from_x -= preview.step_offset;
3143           from_x = (from_x < 0 ? 0 : from_x);
3144         }
3145         else
3146           scroll_direction = MV_UP;
3147         break;
3148
3149       case MV_RIGHT:
3150         if (from_x < level_xsize - preview.xsize)
3151         {
3152           from_x += preview.step_offset;
3153           from_x = (from_x > level_xsize - preview.xsize ?
3154                     level_xsize - preview.xsize : from_x);
3155         }
3156         else
3157           scroll_direction = MV_DOWN;
3158         break;
3159
3160       case MV_UP:
3161         if (from_y > 0)
3162         {
3163           from_y -= preview.step_offset;
3164           from_y = (from_y < 0 ? 0 : from_y);
3165         }
3166         else
3167           scroll_direction = MV_RIGHT;
3168         break;
3169
3170       case MV_DOWN:
3171         if (from_y < level_ysize - preview.ysize)
3172         {
3173           from_y += preview.step_offset;
3174           from_y = (from_y > level_ysize - preview.ysize ?
3175                     level_ysize - preview.ysize : from_y);
3176         }
3177         else
3178           scroll_direction = MV_LEFT;
3179         break;
3180
3181       default:
3182         break;
3183     }
3184
3185     DrawPreviewLevelPlayfieldExt(from_x, from_y);
3186   }
3187
3188   /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
3189   /* redraw micro level label, if needed */
3190   if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3191       !strEqual(level.author, ANONYMOUS_NAME) &&
3192       !strEqual(level.author, leveldir_current->name) &&
3193       DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3194   {
3195     int max_label_counter = 23;
3196
3197     if (leveldir_current->imported_from != NULL &&
3198         strlen(leveldir_current->imported_from) > 0)
3199       max_label_counter += 14;
3200     if (leveldir_current->imported_by != NULL &&
3201         strlen(leveldir_current->imported_by) > 0)
3202       max_label_counter += 14;
3203
3204     label_counter = (label_counter + 1) % max_label_counter;
3205     label_state = (label_counter >= 0 && label_counter <= 7 ?
3206                    MICROLABEL_LEVEL_NAME :
3207                    label_counter >= 9 && label_counter <= 12 ?
3208                    MICROLABEL_LEVEL_AUTHOR_HEAD :
3209                    label_counter >= 14 && label_counter <= 21 ?
3210                    MICROLABEL_LEVEL_AUTHOR :
3211                    label_counter >= 23 && label_counter <= 26 ?
3212                    MICROLABEL_IMPORTED_FROM_HEAD :
3213                    label_counter >= 28 && label_counter <= 35 ?
3214                    MICROLABEL_IMPORTED_FROM :
3215                    label_counter >= 37 && label_counter <= 40 ?
3216                    MICROLABEL_IMPORTED_BY_HEAD :
3217                    label_counter >= 42 && label_counter <= 49 ?
3218                    MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3219
3220     if (leveldir_current->imported_from == NULL &&
3221         (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3222          label_state == MICROLABEL_IMPORTED_FROM))
3223       label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3224                      MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3225
3226     DrawPreviewLevelLabelExt(label_state);
3227   }
3228 }
3229
3230 void DrawPreviewLevelInitial()
3231 {
3232   DrawPreviewLevelExt(TRUE);
3233 }
3234
3235 void DrawPreviewLevelAnimation()
3236 {
3237   DrawPreviewLevelExt(FALSE);
3238 }
3239
3240 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3241                                            int graphic, int sync_frame,
3242                                            int mask_mode)
3243 {
3244   int frame = getGraphicAnimationFrame(graphic, sync_frame);
3245
3246   if (mask_mode == USE_MASKING)
3247     DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3248   else
3249     DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3250 }
3251
3252 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3253                                   int graphic, int sync_frame, int mask_mode)
3254 {
3255   int frame = getGraphicAnimationFrame(graphic, sync_frame);
3256
3257   if (mask_mode == USE_MASKING)
3258     DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3259   else
3260     DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3261 }
3262
3263 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3264 {
3265   int lx = LEVELX(x), ly = LEVELY(y);
3266
3267   if (!IN_SCR_FIELD(x, y))
3268     return;
3269
3270   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3271                           graphic, GfxFrame[lx][ly], NO_MASKING);
3272
3273   MarkTileDirty(x, y);
3274 }
3275
3276 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3277 {
3278   int lx = LEVELX(x), ly = LEVELY(y);
3279
3280   if (!IN_SCR_FIELD(x, y))
3281     return;
3282
3283   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3284                           graphic, GfxFrame[lx][ly], NO_MASKING);
3285   MarkTileDirty(x, y);
3286 }
3287
3288 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3289 {
3290   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3291 }
3292
3293 void DrawLevelElementAnimation(int x, int y, int element)
3294 {
3295   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3296
3297   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3298 }
3299
3300 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3301 {
3302   int sx = SCREENX(x), sy = SCREENY(y);
3303
3304   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3305     return;
3306
3307   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3308     return;
3309
3310   DrawGraphicAnimation(sx, sy, graphic);
3311
3312 #if 1
3313   if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3314     DrawLevelFieldCrumbled(x, y);
3315 #else
3316   if (GFX_CRUMBLED(Feld[x][y]))
3317     DrawLevelFieldCrumbled(x, y);
3318 #endif
3319 }
3320
3321 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3322 {
3323   int sx = SCREENX(x), sy = SCREENY(y);
3324   int graphic;
3325
3326   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3327     return;
3328
3329   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3330
3331   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3332     return;
3333
3334   DrawGraphicAnimation(sx, sy, graphic);
3335
3336   if (GFX_CRUMBLED(element))
3337     DrawLevelFieldCrumbled(x, y);
3338 }
3339
3340 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3341 {
3342   if (player->use_murphy)
3343   {
3344     /* this works only because currently only one player can be "murphy" ... */
3345     static int last_horizontal_dir = MV_LEFT;
3346     int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3347
3348     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3349       last_horizontal_dir = move_dir;
3350
3351     if (graphic == IMG_SP_MURPHY)       /* undefined => use special graphic */
3352     {
3353       int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3354
3355       graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3356     }
3357
3358     return graphic;
3359   }
3360   else
3361     return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3362 }
3363
3364 static boolean equalGraphics(int graphic1, int graphic2)
3365 {
3366   struct GraphicInfo *g1 = &graphic_info[graphic1];
3367   struct GraphicInfo *g2 = &graphic_info[graphic2];
3368
3369   return (g1->bitmap      == g2->bitmap &&
3370           g1->src_x       == g2->src_x &&
3371           g1->src_y       == g2->src_y &&
3372           g1->anim_frames == g2->anim_frames &&
3373           g1->anim_delay  == g2->anim_delay &&
3374           g1->anim_mode   == g2->anim_mode);
3375 }
3376
3377 void DrawAllPlayers()
3378 {
3379   int i;
3380
3381   for (i = 0; i < MAX_PLAYERS; i++)
3382     if (stored_player[i].active)
3383       DrawPlayer(&stored_player[i]);
3384 }
3385
3386 void DrawPlayerField(int x, int y)
3387 {
3388   if (!IS_PLAYER(x, y))
3389     return;
3390
3391   DrawPlayer(PLAYERINFO(x, y));
3392 }
3393
3394 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3395
3396 void DrawPlayer(struct PlayerInfo *player)
3397 {
3398   int jx = player->jx;
3399   int jy = player->jy;
3400   int move_dir = player->MovDir;
3401   int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3402   int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? +1 : 0);
3403   int last_jx = (player->is_moving ? jx - dx : jx);
3404   int last_jy = (player->is_moving ? jy - dy : jy);
3405   int next_jx = jx + dx;
3406   int next_jy = jy + dy;
3407   boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3408   boolean player_is_opaque = FALSE;
3409   int sx = SCREENX(jx), sy = SCREENY(jy);
3410   int sxx = 0, syy = 0;
3411   int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3412   int graphic;
3413   int action = ACTION_DEFAULT;
3414   int last_player_graphic = getPlayerGraphic(player, move_dir);
3415   int last_player_frame = player->Frame;
3416   int frame = 0;
3417
3418   /* GfxElement[][] is set to the element the player is digging or collecting;
3419      remove also for off-screen player if the player is not moving anymore */
3420   if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3421     GfxElement[jx][jy] = EL_UNDEFINED;
3422
3423   if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3424     return;
3425
3426 #if DEBUG
3427   if (!IN_LEV_FIELD(jx, jy))
3428   {
3429     printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3430     printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3431     printf("DrawPlayerField(): This should never happen!\n");
3432     return;
3433   }
3434 #endif
3435
3436   if (element == EL_EXPLOSION)
3437     return;
3438
3439   action = (player->is_pushing    ? ACTION_PUSHING         :
3440             player->is_digging    ? ACTION_DIGGING         :
3441             player->is_collecting ? ACTION_COLLECTING      :
3442             player->is_moving     ? ACTION_MOVING          :
3443             player->is_snapping   ? ACTION_SNAPPING        :
3444             player->is_dropping   ? ACTION_DROPPING        :
3445             player->is_waiting    ? player->action_waiting : ACTION_DEFAULT);
3446
3447   if (player->is_waiting)
3448     move_dir = player->dir_waiting;
3449
3450   InitPlayerGfxAnimation(player, action, move_dir);
3451
3452   /* ----------------------------------------------------------------------- */
3453   /* draw things in the field the player is leaving, if needed               */
3454   /* ----------------------------------------------------------------------- */
3455
3456   if (player->is_moving)
3457   {
3458     if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3459     {
3460       DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3461
3462       if (last_element == EL_DYNAMITE_ACTIVE ||
3463           last_element == EL_EM_DYNAMITE_ACTIVE ||
3464           last_element == EL_SP_DISK_RED_ACTIVE)
3465         DrawDynamite(last_jx, last_jy);
3466       else
3467         DrawLevelFieldThruMask(last_jx, last_jy);
3468     }
3469     else if (last_element == EL_DYNAMITE_ACTIVE ||
3470              last_element == EL_EM_DYNAMITE_ACTIVE ||
3471              last_element == EL_SP_DISK_RED_ACTIVE)
3472       DrawDynamite(last_jx, last_jy);
3473 #if 0
3474     /* !!! this is not enough to prevent flickering of players which are
3475        moving next to each others without a free tile between them -- this
3476        can only be solved by drawing all players layer by layer (first the
3477        background, then the foreground etc.) !!! => TODO */
3478     else if (!IS_PLAYER(last_jx, last_jy))
3479       DrawLevelField(last_jx, last_jy);
3480 #else
3481     else
3482       DrawLevelField(last_jx, last_jy);
3483 #endif
3484
3485     if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3486       DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3487   }
3488
3489   if (!IN_SCR_FIELD(sx, sy))
3490     return;
3491
3492   /* ----------------------------------------------------------------------- */
3493   /* draw things behind the player, if needed                                */
3494   /* ----------------------------------------------------------------------- */
3495
3496   if (Back[jx][jy])
3497     DrawLevelElement(jx, jy, Back[jx][jy]);
3498   else if (IS_ACTIVE_BOMB(element))
3499     DrawLevelElement(jx, jy, EL_EMPTY);
3500   else
3501   {
3502     if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3503     {
3504       int old_element = GfxElement[jx][jy];
3505       int old_graphic = el_act_dir2img(old_element, action, move_dir);
3506       int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3507
3508       if (GFX_CRUMBLED(old_element))
3509         DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3510       else
3511         DrawGraphic(sx, sy, old_graphic, frame);
3512
3513       if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3514         player_is_opaque = TRUE;
3515     }
3516     else
3517     {
3518       GfxElement[jx][jy] = EL_UNDEFINED;
3519
3520       /* make sure that pushed elements are drawn with correct frame rate */
3521       graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3522
3523       if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3524         GfxFrame[jx][jy] = player->StepFrame;
3525
3526       DrawLevelField(jx, jy);
3527     }
3528   }
3529
3530 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3531   /* ----------------------------------------------------------------------- */
3532   /* draw player himself                                                     */
3533   /* ----------------------------------------------------------------------- */
3534
3535   graphic = getPlayerGraphic(player, move_dir);
3536
3537   /* in the case of changed player action or direction, prevent the current
3538      animation frame from being restarted for identical animations */
3539   if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3540     player->Frame = last_player_frame;
3541
3542   frame = getGraphicAnimationFrame(graphic, player->Frame);
3543
3544   if (player->GfxPos)
3545   {
3546     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3547       sxx = player->GfxPos;
3548     else
3549       syy = player->GfxPos;
3550   }
3551
3552   if (player_is_opaque)
3553     DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3554   else
3555     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3556
3557   if (SHIELD_ON(player))
3558   {
3559     int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3560                    IMG_SHIELD_NORMAL_ACTIVE);
3561     int frame = getGraphicAnimationFrame(graphic, -1);
3562
3563     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3564   }
3565 #endif
3566
3567 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3568   if (player->GfxPos)
3569   {
3570     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3571       sxx = player->GfxPos;
3572     else
3573       syy = player->GfxPos;
3574   }
3575 #endif
3576
3577   /* ----------------------------------------------------------------------- */
3578   /* draw things the player is pushing, if needed                            */
3579   /* ----------------------------------------------------------------------- */
3580
3581   if (player->is_pushing && player->is_moving)
3582   {
3583     int px = SCREENX(jx), py = SCREENY(jy);
3584     int pxx = (TILEX - ABS(sxx)) * dx;
3585     int pyy = (TILEY - ABS(syy)) * dy;
3586     int gfx_frame = GfxFrame[jx][jy];
3587
3588     int graphic;
3589     int sync_frame;
3590     int frame;
3591
3592     if (!IS_MOVING(jx, jy))             /* push movement already finished */
3593     {
3594       element = Feld[next_jx][next_jy];
3595       gfx_frame = GfxFrame[next_jx][next_jy];
3596     }
3597
3598     graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3599
3600     sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3601     frame = getGraphicAnimationFrame(graphic, sync_frame);
3602
3603     /* draw background element under pushed element (like the Sokoban field) */
3604     if (game.use_masked_pushing && IS_MOVING(jx, jy))
3605     {
3606       /* this allows transparent pushing animation over non-black background */
3607
3608       if (Back[jx][jy])
3609         DrawLevelElement(jx, jy, Back[jx][jy]);
3610       else
3611         DrawLevelElement(jx, jy, EL_EMPTY);
3612
3613       if (Back[next_jx][next_jy])
3614         DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3615       else
3616         DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3617     }
3618     else if (Back[next_jx][next_jy])
3619       DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3620
3621 #if 1
3622     /* do not draw (EM style) pushing animation when pushing is finished */
3623     /* (two-tile animations usually do not contain start and end frame) */
3624     if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3625       DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3626     else
3627       DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3628 #else
3629     /* masked drawing is needed for EMC style (double) movement graphics */
3630     /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3631     DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3632 #endif
3633   }
3634
3635 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3636   /* ----------------------------------------------------------------------- */
3637   /* draw player himself                                                     */
3638   /* ----------------------------------------------------------------------- */
3639
3640   graphic = getPlayerGraphic(player, move_dir);
3641
3642   /* in the case of changed player action or direction, prevent the current
3643      animation frame from being restarted for identical animations */
3644   if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3645     player->Frame = last_player_frame;
3646
3647   frame = getGraphicAnimationFrame(graphic, player->Frame);
3648
3649   if (player->GfxPos)
3650   {
3651     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3652       sxx = player->GfxPos;
3653     else
3654       syy = player->GfxPos;
3655   }
3656
3657   if (player_is_opaque)
3658     DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3659   else
3660     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3661
3662   if (SHIELD_ON(player))
3663   {
3664     int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3665                    IMG_SHIELD_NORMAL_ACTIVE);
3666     int frame = getGraphicAnimationFrame(graphic, -1);
3667
3668     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3669   }
3670 #endif
3671
3672   /* ----------------------------------------------------------------------- */
3673   /* draw things in front of player (active dynamite or dynabombs)           */
3674   /* ----------------------------------------------------------------------- */
3675
3676   if (IS_ACTIVE_BOMB(element))
3677   {
3678     graphic = el2img(element);
3679     frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3680
3681     if (game.emulation == EMU_SUPAPLEX)
3682       DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3683     else
3684       DrawGraphicThruMask(sx, sy, graphic, frame);
3685   }
3686
3687   if (player_is_moving && last_element == EL_EXPLOSION)
3688   {
3689     int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3690                    GfxElement[last_jx][last_jy] :  EL_EMPTY);
3691     int graphic = el_act2img(element, ACTION_EXPLODING);
3692     int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3693     int phase = ExplodePhase[last_jx][last_jy] - 1;
3694     int frame = getGraphicAnimationFrame(graphic, phase - delay);
3695
3696     if (phase >= delay)
3697       DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3698   }
3699
3700   /* ----------------------------------------------------------------------- */
3701   /* draw elements the player is just walking/passing through/under          */
3702   /* ----------------------------------------------------------------------- */
3703
3704   if (player_is_moving)
3705   {
3706     /* handle the field the player is leaving ... */
3707     if (IS_ACCESSIBLE_INSIDE(last_element))
3708       DrawLevelField(last_jx, last_jy);
3709     else if (IS_ACCESSIBLE_UNDER(last_element))
3710       DrawLevelFieldThruMask(last_jx, last_jy);
3711   }
3712
3713   /* do not redraw accessible elements if the player is just pushing them */
3714   if (!player_is_moving || !player->is_pushing)
3715   {
3716     /* ... and the field the player is entering */
3717     if (IS_ACCESSIBLE_INSIDE(element))
3718       DrawLevelField(jx, jy);
3719     else if (IS_ACCESSIBLE_UNDER(element))
3720       DrawLevelFieldThruMask(jx, jy);
3721   }
3722
3723   MarkTileDirty(sx, sy);
3724 }
3725
3726 /* ------------------------------------------------------------------------- */
3727
3728 void WaitForEventToContinue()
3729 {
3730   boolean still_wait = TRUE;
3731
3732   /* simulate releasing mouse button over last gadget, if still pressed */
3733   if (button_status)
3734     HandleGadgets(-1, -1, 0);
3735
3736   button_status = MB_RELEASED;
3737
3738   ClearEventQueue();
3739
3740   while (still_wait)
3741   {
3742     if (PendingEvent())
3743     {
3744       Event event;
3745
3746       NextEvent(&event);
3747
3748       switch (event.type)
3749       {
3750         case EVENT_BUTTONPRESS:
3751         case EVENT_KEYPRESS:
3752           still_wait = FALSE;
3753           break;
3754
3755         case EVENT_KEYRELEASE:
3756           ClearPlayerAction();
3757           break;
3758
3759         default:
3760           HandleOtherEvents(&event);
3761           break;
3762       }
3763     }
3764     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3765     {
3766       still_wait = FALSE;
3767     }
3768
3769     BackToFront();
3770   }
3771 }
3772
3773 #define MAX_REQUEST_LINES               13
3774 #define MAX_REQUEST_LINE_FONT1_LEN      7
3775 #define MAX_REQUEST_LINE_FONT2_LEN      10
3776
3777 static int RequestHandleEvents(unsigned int req_state)
3778 {
3779   boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3780                           local_player->LevelSolved_GameEnd);
3781   int width  = request.width;
3782   int height = request.height;
3783   int sx, sy;
3784   int result;
3785
3786   setRequestPosition(&sx, &sy, FALSE);
3787
3788   button_status = MB_RELEASED;
3789
3790   request_gadget_id = -1;
3791   result = -1;
3792
3793   while (result < 0)
3794   {
3795     if (level_solved)
3796     {
3797       SetDrawtoField(DRAW_TO_FIELDBUFFER);
3798
3799       HandleGameActions();
3800
3801       SetDrawtoField(DRAW_TO_BACKBUFFER);
3802
3803       if (global.use_envelope_request)
3804       {
3805         /* copy current state of request area to middle of playfield area */
3806         BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
3807       }
3808     }
3809
3810     if (PendingEvent())
3811     {
3812       Event event;
3813
3814       while (NextValidEvent(&event))
3815       {
3816         switch (event.type)
3817         {
3818           case EVENT_BUTTONPRESS:
3819           case EVENT_BUTTONRELEASE:
3820           case EVENT_MOTIONNOTIFY:
3821           {
3822             int mx, my;
3823
3824             if (event.type == EVENT_MOTIONNOTIFY)
3825             {
3826               if (!button_status)
3827                 continue;
3828
3829               motion_status = TRUE;
3830               mx = ((MotionEvent *) &event)->x;
3831               my = ((MotionEvent *) &event)->y;
3832             }
3833             else
3834             {
3835               motion_status = FALSE;
3836               mx = ((ButtonEvent *) &event)->x;
3837               my = ((ButtonEvent *) &event)->y;
3838               if (event.type == EVENT_BUTTONPRESS)
3839                 button_status = ((ButtonEvent *) &event)->button;
3840               else
3841                 button_status = MB_RELEASED;
3842             }
3843
3844             /* this sets 'request_gadget_id' */
3845             HandleGadgets(mx, my, button_status);
3846
3847             switch (request_gadget_id)
3848             {
3849               case TOOL_CTRL_ID_YES:
3850                 result = TRUE;
3851                 break;
3852               case TOOL_CTRL_ID_NO:
3853                 result = FALSE;
3854                 break;
3855               case TOOL_CTRL_ID_CONFIRM:
3856                 result = TRUE | FALSE;
3857                 break;
3858
3859               case TOOL_CTRL_ID_PLAYER_1:
3860                 result = 1;
3861                 break;
3862               case TOOL_CTRL_ID_PLAYER_2:
3863                 result = 2;
3864                 break;
3865               case TOOL_CTRL_ID_PLAYER_3:
3866                 result = 3;
3867                 break;
3868               case TOOL_CTRL_ID_PLAYER_4:
3869                 result = 4;
3870                 break;
3871
3872               default:
3873                 break;
3874             }
3875
3876             break;
3877           }
3878
3879           case EVENT_KEYPRESS:
3880           {
3881             Key key = GetEventKey((KeyEvent *)&event, TRUE);
3882
3883             switch (key)
3884             {
3885               case KSYM_space:
3886                 if (req_state & REQ_CONFIRM)
3887                   result = 1;
3888                 break;
3889
3890               case KSYM_Return:
3891 #if defined(TARGET_SDL2)
3892               case KSYM_Menu:
3893 #endif
3894                 result = 1;
3895                 break;
3896
3897               case KSYM_Escape:
3898 #if defined(TARGET_SDL2)
3899               case KSYM_Back:
3900 #endif
3901                 result = 0;
3902                 break;
3903
3904               default:
3905                 HandleKeysDebug(key);
3906                 break;
3907             }
3908
3909             if (req_state & REQ_PLAYER)
3910               result = 0;
3911
3912             break;
3913           }
3914
3915           case EVENT_KEYRELEASE:
3916             ClearPlayerAction();
3917             break;
3918
3919           default:
3920             HandleOtherEvents(&event);
3921             break;
3922         }
3923       }
3924     }
3925     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3926     {
3927       int joy = AnyJoystick();
3928
3929       if (joy & JOY_BUTTON_1)
3930         result = 1;
3931       else if (joy & JOY_BUTTON_2)
3932         result = 0;
3933     }
3934
3935     if (level_solved)
3936     {
3937       if (global.use_envelope_request)
3938       {
3939         /* copy back current state of pressed buttons inside request area */
3940         BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
3941       }
3942     }
3943
3944     BackToFront();
3945   }
3946
3947   return result;
3948 }
3949
3950 static boolean RequestDoor(char *text, unsigned int req_state)
3951 {
3952   unsigned int old_door_state;
3953   int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3954   int font_nr = FONT_TEXT_2;
3955   char *text_ptr;
3956   int result;
3957   int ty;
3958
3959   if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3960   {
3961     max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3962     font_nr = FONT_TEXT_1;
3963   }
3964
3965   if (game_status == GAME_MODE_PLAYING)
3966     BlitScreenToBitmap(backbuffer);
3967
3968   /* disable deactivated drawing when quick-loading level tape recording */
3969   if (tape.playing && tape.deactivate_display)
3970     TapeDeactivateDisplayOff(TRUE);
3971
3972   SetMouseCursor(CURSOR_DEFAULT);
3973
3974 #if defined(NETWORK_AVALIABLE)
3975   /* pause network game while waiting for request to answer */
3976   if (options.network &&
3977       game_status == GAME_MODE_PLAYING &&
3978       req_state & REQUEST_WAIT_FOR_INPUT)
3979     SendToServer_PausePlaying();
3980 #endif
3981
3982   old_door_state = GetDoorState();
3983
3984   /* simulate releasing mouse button over last gadget, if still pressed */
3985   if (button_status)
3986     HandleGadgets(-1, -1, 0);
3987
3988   UnmapAllGadgets();
3989
3990   /* draw released gadget before proceeding */
3991   // BackToFront();
3992
3993   if (old_door_state & DOOR_OPEN_1)
3994   {
3995     CloseDoor(DOOR_CLOSE_1);
3996
3997     /* save old door content */
3998     BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3999                0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4000   }
4001
4002   SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4003   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4004
4005   /* clear door drawing field */
4006   DrawBackground(DX, DY, DXSIZE, DYSIZE);
4007
4008   /* force DOOR font inside door area */
4009   SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4010
4011   /* write text for request */
4012   for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4013   {
4014     char text_line[max_request_line_len + 1];
4015     int tx, tl, tc = 0;
4016
4017     if (!*text_ptr)
4018       break;
4019
4020     for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4021     {
4022       tc = *(text_ptr + tx);
4023       // if (!tc || tc == ' ')
4024       if (!tc || tc == ' ' || tc == '?' || tc == '!')
4025         break;
4026     }
4027
4028     if ((tc == '?' || tc == '!') && tl == 0)
4029       tl = 1;
4030
4031     if (!tl)
4032     { 
4033       text_ptr++; 
4034       ty--; 
4035       continue; 
4036     }
4037
4038     strncpy(text_line, text_ptr, tl);
4039     text_line[tl] = 0;
4040
4041     DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4042              DY + 8 + ty * (getFontHeight(font_nr) + 2),
4043              text_line, font_nr);
4044
4045     text_ptr += tl + (tc == ' ' ? 1 : 0);
4046     // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4047   }
4048
4049   ResetFontStatus();
4050
4051   if (req_state & REQ_ASK)
4052   {
4053     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4054     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4055   }
4056   else if (req_state & REQ_CONFIRM)
4057   {
4058     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4059   }
4060   else if (req_state & REQ_PLAYER)
4061   {
4062     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4063     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4064     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4065     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4066   }
4067
4068   /* copy request gadgets to door backbuffer */
4069   BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4070
4071   OpenDoor(DOOR_OPEN_1);
4072
4073   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4074   {
4075     if (game_status == GAME_MODE_PLAYING)
4076     {
4077       SetPanelBackground();
4078       SetDrawBackgroundMask(REDRAW_DOOR_1);
4079     }
4080     else
4081     {
4082       SetDrawBackgroundMask(REDRAW_FIELD);
4083     }
4084
4085     return FALSE;
4086   }
4087
4088   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4089
4090   // ---------- handle request buttons ----------
4091   result = RequestHandleEvents(req_state);
4092
4093   UnmapToolButtons();
4094
4095   if (!(req_state & REQ_STAY_OPEN))
4096   {
4097     CloseDoor(DOOR_CLOSE_1);
4098
4099     if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4100         (req_state & REQ_REOPEN))
4101       OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4102   }
4103
4104   RemapAllGadgets();
4105
4106   if (game_status == GAME_MODE_PLAYING)
4107   {
4108     SetPanelBackground();
4109     SetDrawBackgroundMask(REDRAW_DOOR_1);
4110   }
4111   else
4112   {
4113     SetDrawBackgroundMask(REDRAW_FIELD);
4114   }
4115
4116 #if defined(NETWORK_AVALIABLE)
4117   /* continue network game after request */
4118   if (options.network &&
4119       game_status == GAME_MODE_PLAYING &&
4120       req_state & REQUEST_WAIT_FOR_INPUT)
4121     SendToServer_ContinuePlaying();
4122 #endif
4123
4124   /* restore deactivated drawing when quick-loading level tape recording */
4125   if (tape.playing && tape.deactivate_display)
4126     TapeDeactivateDisplayOn();
4127
4128   return result;
4129 }
4130
4131 static boolean RequestEnvelope(char *text, unsigned int req_state)
4132 {
4133   int result;
4134
4135   if (game_status == GAME_MODE_PLAYING)
4136     BlitScreenToBitmap(backbuffer);
4137
4138   /* disable deactivated drawing when quick-loading level tape recording */
4139   if (tape.playing && tape.deactivate_display)
4140     TapeDeactivateDisplayOff(TRUE);
4141
4142   SetMouseCursor(CURSOR_DEFAULT);
4143
4144 #if defined(NETWORK_AVALIABLE)
4145   /* pause network game while waiting for request to answer */
4146   if (options.network &&
4147       game_status == GAME_MODE_PLAYING &&
4148       req_state & REQUEST_WAIT_FOR_INPUT)
4149     SendToServer_PausePlaying();
4150 #endif
4151
4152   /* simulate releasing mouse button over last gadget, if still pressed */
4153   if (button_status)
4154     HandleGadgets(-1, -1, 0);
4155
4156   UnmapAllGadgets();
4157
4158   // (replace with setting corresponding request background)
4159   // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4160   // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4161
4162   /* clear door drawing field */
4163   // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4164
4165   ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4166
4167   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4168   {
4169     if (game_status == GAME_MODE_PLAYING)
4170     {
4171       SetPanelBackground();
4172       SetDrawBackgroundMask(REDRAW_DOOR_1);
4173     }
4174     else
4175     {
4176       SetDrawBackgroundMask(REDRAW_FIELD);
4177     }
4178
4179     return FALSE;
4180   }
4181
4182   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4183
4184   // ---------- handle request buttons ----------
4185   result = RequestHandleEvents(req_state);
4186
4187   UnmapToolButtons();
4188
4189   ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4190
4191   RemapAllGadgets();
4192
4193   if (game_status == GAME_MODE_PLAYING)
4194   {
4195     SetPanelBackground();
4196     SetDrawBackgroundMask(REDRAW_DOOR_1);
4197   }
4198   else
4199   {
4200     SetDrawBackgroundMask(REDRAW_FIELD);
4201   }
4202
4203 #if defined(NETWORK_AVALIABLE)
4204   /* continue network game after request */
4205   if (options.network &&
4206       game_status == GAME_MODE_PLAYING &&
4207       req_state & REQUEST_WAIT_FOR_INPUT)
4208     SendToServer_ContinuePlaying();
4209 #endif
4210
4211   /* restore deactivated drawing when quick-loading level tape recording */
4212   if (tape.playing && tape.deactivate_display)
4213     TapeDeactivateDisplayOn();
4214
4215   return result;
4216 }
4217
4218 boolean Request(char *text, unsigned int req_state)
4219 {
4220   boolean overlay_active = GetOverlayActive();
4221   boolean result;
4222
4223   SetOverlayActive(FALSE);
4224
4225   if (global.use_envelope_request)
4226     result = RequestEnvelope(text, req_state);
4227   else
4228     result = RequestDoor(text, req_state);
4229
4230   SetOverlayActive(overlay_active);
4231
4232   return result;
4233 }
4234
4235 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4236 {
4237   const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4238   const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4239   int compare_result;
4240
4241   if (dpo1->sort_priority != dpo2->sort_priority)
4242     compare_result = dpo1->sort_priority - dpo2->sort_priority;
4243   else
4244     compare_result = dpo1->nr - dpo2->nr;
4245
4246   return compare_result;
4247 }
4248
4249 void InitGraphicCompatibilityInfo_Doors()
4250 {
4251   struct
4252   {
4253     int door_token;
4254     int part_1, part_8;
4255     struct DoorInfo *door;
4256   }
4257   doors[] =
4258   {
4259     { DOOR_1,   IMG_GFX_DOOR_1_PART_1,  IMG_GFX_DOOR_1_PART_8,  &door_1 },
4260     { DOOR_2,   IMG_GFX_DOOR_2_PART_1,  IMG_GFX_DOOR_2_PART_8,  &door_2 },
4261
4262     { -1,       -1,                     -1,                     NULL    }
4263   };
4264   struct Rect door_rect_list[] =
4265   {
4266     { DX, DY, DXSIZE, DYSIZE },
4267     { VX, VY, VXSIZE, VYSIZE }
4268   };
4269   int i, j;
4270
4271   for (i = 0; doors[i].door_token != -1; i++)
4272   {
4273     int door_token = doors[i].door_token;
4274     int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4275     int part_1 = doors[i].part_1;
4276     int part_8 = doors[i].part_8;
4277     int part_2 = part_1 + 1;
4278     int part_3 = part_1 + 2;
4279     struct DoorInfo *door = doors[i].door;
4280     struct Rect *door_rect = &door_rect_list[door_index];
4281     boolean door_gfx_redefined = FALSE;
4282
4283     /* check if any door part graphic definitions have been redefined */
4284
4285     for (j = 0; door_part_controls[j].door_token != -1; j++)
4286     {
4287       struct DoorPartControlInfo *dpc = &door_part_controls[j];
4288       struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4289
4290       if (dpc->door_token == door_token && fi->redefined)
4291         door_gfx_redefined = TRUE;
4292     }
4293
4294     /* check for old-style door graphic/animation modifications */
4295
4296     if (!door_gfx_redefined)
4297     {
4298       if (door->anim_mode & ANIM_STATIC_PANEL)
4299       {
4300         door->panel.step_xoffset = 0;
4301         door->panel.step_yoffset = 0;
4302       }
4303
4304       if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4305       {
4306         struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4307         struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4308         int num_door_steps, num_panel_steps;
4309
4310         /* remove door part graphics other than the two default wings */
4311
4312         for (j = 0; door_part_controls[j].door_token != -1; j++)
4313         {
4314           struct DoorPartControlInfo *dpc = &door_part_controls[j];
4315           struct GraphicInfo *g = &graphic_info[dpc->graphic];
4316
4317           if (dpc->graphic >= part_3 &&
4318               dpc->graphic <= part_8)
4319             g->bitmap = NULL;
4320         }
4321
4322         /* set graphics and screen positions of the default wings */
4323
4324         g_part_1->width  = door_rect->width;
4325         g_part_1->height = door_rect->height;
4326         g_part_2->width  = door_rect->width;
4327         g_part_2->height = door_rect->height;
4328         g_part_2->src_x = door_rect->width;
4329         g_part_2->src_y = g_part_1->src_y;
4330
4331         door->part_2.x = door->part_1.x;
4332         door->part_2.y = door->part_1.y;
4333
4334         if (door->width != -1)
4335         {
4336           g_part_1->width = door->width;
4337           g_part_2->width = door->width;
4338
4339           // special treatment for graphics and screen position of right wing
4340           g_part_2->src_x += door_rect->width - door->width;
4341           door->part_2.x  += door_rect->width - door->width;
4342         }
4343
4344         if (door->height != -1)
4345         {
4346           g_part_1->height = door->height;
4347           g_part_2->height = door->height;
4348
4349           // special treatment for graphics and screen position of bottom wing
4350           g_part_2->src_y += door_rect->height - door->height;
4351           door->part_2.y  += door_rect->height - door->height;
4352         }
4353
4354         /* set animation delays for the default wings and panels */
4355
4356         door->part_1.step_delay = door->step_delay;
4357         door->part_2.step_delay = door->step_delay;
4358         door->panel.step_delay  = door->step_delay;
4359
4360         /* set animation draw order for the default wings */
4361
4362         door->part_1.sort_priority = 2; /* draw left wing over ... */
4363         door->part_2.sort_priority = 1; /*          ... right wing */
4364
4365         /* set animation draw offset for the default wings */
4366
4367         if (door->anim_mode & ANIM_HORIZONTAL)
4368         {
4369           door->part_1.step_xoffset = door->step_offset;
4370           door->part_1.step_yoffset = 0;
4371           door->part_2.step_xoffset = door->step_offset * -1;
4372           door->part_2.step_yoffset = 0;
4373
4374           num_door_steps = g_part_1->width / door->step_offset;
4375         }
4376         else    // ANIM_VERTICAL
4377         {
4378           door->part_1.step_xoffset = 0;
4379           door->part_1.step_yoffset = door->step_offset;
4380           door->part_2.step_xoffset = 0;
4381           door->part_2.step_yoffset = door->step_offset * -1;
4382
4383           num_door_steps = g_part_1->height / door->step_offset;
4384         }
4385
4386         /* set animation draw offset for the default panels */
4387
4388         if (door->step_offset > 1)
4389         {
4390           num_panel_steps = 2 * door_rect->height / door->step_offset;
4391           door->panel.start_step = num_panel_steps - num_door_steps;
4392           door->panel.start_step_closing = door->panel.start_step;
4393         }
4394         else
4395         {
4396           num_panel_steps = door_rect->height / door->step_offset;
4397           door->panel.start_step = num_panel_steps - num_door_steps / 2;
4398           door->panel.start_step_closing = door->panel.start_step;
4399           door->panel.step_delay *= 2;
4400         }
4401       }
4402     }
4403   }
4404 }
4405
4406 void InitDoors()
4407 {
4408   int i;
4409
4410   for (i = 0; door_part_controls[i].door_token != -1; i++)
4411   {
4412     struct DoorPartControlInfo *dpc = &door_part_controls[i];
4413     struct DoorPartOrderInfo *dpo = &door_part_order[i];
4414
4415     /* initialize "start_step_opening" and "start_step_closing", if needed */
4416     if (dpc->pos->start_step_opening == 0 &&
4417         dpc->pos->start_step_closing == 0)
4418     {
4419       // dpc->pos->start_step_opening = dpc->pos->start_step;
4420       dpc->pos->start_step_closing = dpc->pos->start_step;
4421     }
4422
4423     /* fill structure for door part draw order (sorted below) */
4424     dpo->nr = i;
4425     dpo->sort_priority = dpc->pos->sort_priority;
4426   }
4427
4428   /* sort door part controls according to sort_priority and graphic number */
4429   qsort(door_part_order, MAX_DOOR_PARTS,
4430         sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4431 }
4432
4433 unsigned int OpenDoor(unsigned int door_state)
4434 {
4435   if (door_state & DOOR_COPY_BACK)
4436   {
4437     if (door_state & DOOR_OPEN_1)
4438       BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4439                  1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4440
4441     if (door_state & DOOR_OPEN_2)
4442       BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4443                  1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4444
4445     door_state &= ~DOOR_COPY_BACK;
4446   }
4447
4448   return MoveDoor(door_state);
4449 }
4450
4451 unsigned int CloseDoor(unsigned int door_state)
4452 {
4453   unsigned int old_door_state = GetDoorState();
4454
4455   if (!(door_state & DOOR_NO_COPY_BACK))
4456   {
4457     if (old_door_state & DOOR_OPEN_1)
4458       BlitBitmap(backbuffer, bitmap_db_door_1,
4459                  DX, DY, DXSIZE, DYSIZE, 0, 0);
4460
4461     if (old_door_state & DOOR_OPEN_2)
4462       BlitBitmap(backbuffer, bitmap_db_door_2,
4463                  VX, VY, VXSIZE, VYSIZE, 0, 0);
4464
4465     door_state &= ~DOOR_NO_COPY_BACK;
4466   }
4467
4468   return MoveDoor(door_state);
4469 }
4470
4471 unsigned int GetDoorState()
4472 {
4473   return MoveDoor(DOOR_GET_STATE);
4474 }
4475
4476 unsigned int SetDoorState(unsigned int door_state)
4477 {
4478   return MoveDoor(door_state | DOOR_SET_STATE);
4479 }
4480
4481 int euclid(int a, int b)
4482 {
4483   return (b ? euclid(b, a % b) : a);
4484 }
4485
4486 unsigned int MoveDoor(unsigned int door_state)
4487 {
4488   struct Rect door_rect_list[] =
4489   {
4490     { DX, DY, DXSIZE, DYSIZE },
4491     { VX, VY, VXSIZE, VYSIZE }
4492   };
4493   static int door1 = DOOR_CLOSE_1;
4494   static int door2 = DOOR_CLOSE_2;
4495   unsigned int door_delay = 0;
4496   unsigned int door_delay_value;
4497   int i;
4498
4499   if (door_state == DOOR_GET_STATE)
4500     return (door1 | door2);
4501
4502   if (door_state & DOOR_SET_STATE)
4503   {
4504     if (door_state & DOOR_ACTION_1)
4505       door1 = door_state & DOOR_ACTION_1;
4506     if (door_state & DOOR_ACTION_2)
4507       door2 = door_state & DOOR_ACTION_2;
4508
4509     return (door1 | door2);
4510   }
4511
4512   if (!(door_state & DOOR_FORCE_REDRAW))
4513   {
4514     if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4515       door_state &= ~DOOR_OPEN_1;
4516     else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4517       door_state &= ~DOOR_CLOSE_1;
4518     if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4519       door_state &= ~DOOR_OPEN_2;
4520     else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4521       door_state &= ~DOOR_CLOSE_2;
4522   }
4523
4524   if (global.autoplay_leveldir)
4525   {
4526     door_state |= DOOR_NO_DELAY;
4527     door_state &= ~DOOR_CLOSE_ALL;
4528   }
4529
4530   if (game_status == GAME_MODE_EDITOR)
4531     door_state |= DOOR_NO_DELAY;
4532
4533   if (door_state & DOOR_ACTION)
4534   {
4535     boolean door_panel_drawn[NUM_DOORS];
4536     boolean panel_has_doors[NUM_DOORS];
4537     boolean door_part_skip[MAX_DOOR_PARTS];
4538     boolean door_part_done[MAX_DOOR_PARTS];
4539     boolean door_part_done_all;
4540     int num_steps[MAX_DOOR_PARTS];
4541     int max_move_delay = 0;     // delay for complete animations of all doors
4542     int max_step_delay = 0;     // delay (ms) between two animation frames
4543     int num_move_steps = 0;     // number of animation steps for all doors
4544     int max_move_delay_doors_only = 0;  // delay for doors only (no panel)
4545     int num_move_steps_doors_only = 0;  // steps for doors only (no panel)
4546     int current_move_delay = 0;
4547     int start = 0;
4548     int k;
4549
4550     for (i = 0; i < NUM_DOORS; i++)
4551       panel_has_doors[i] = FALSE;
4552
4553     for (i = 0; i < MAX_DOOR_PARTS; i++)
4554     {
4555       struct DoorPartControlInfo *dpc = &door_part_controls[i];
4556       struct GraphicInfo *g = &graphic_info[dpc->graphic];
4557       int door_token = dpc->door_token;
4558
4559       door_part_done[i] = FALSE;
4560       door_part_skip[i] = (!(door_state & door_token) ||
4561                            !g->bitmap);
4562     }
4563
4564     for (i = 0; i < MAX_DOOR_PARTS; i++)
4565     {
4566       int nr = door_part_order[i].nr;
4567       struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4568       struct DoorPartPosInfo *pos = dpc->pos;
4569       struct GraphicInfo *g = &graphic_info[dpc->graphic];
4570       int door_token = dpc->door_token;
4571       int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4572       boolean is_panel = DOOR_PART_IS_PANEL(nr);
4573       int step_xoffset = ABS(pos->step_xoffset);
4574       int step_yoffset = ABS(pos->step_yoffset);
4575       int step_delay = pos->step_delay;
4576       int current_door_state = door_state & door_token;
4577       boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
4578       boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4579       boolean part_opening = (is_panel ? door_closing : door_opening);
4580       int start_step = (part_opening ? pos->start_step_opening :
4581                         pos->start_step_closing);
4582       float move_xsize = (step_xoffset ? g->width  : 0);
4583       float move_ysize = (step_yoffset ? g->height : 0);
4584       int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4585       int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4586       int move_steps = (move_xsteps && move_ysteps ?
4587                         MIN(move_xsteps, move_ysteps) :
4588                         move_xsteps ? move_xsteps : move_ysteps) - start_step;
4589       int move_delay = move_steps * step_delay;
4590
4591       if (door_part_skip[nr])
4592         continue;
4593
4594       max_move_delay = MAX(max_move_delay, move_delay);
4595       max_step_delay = (max_step_delay == 0 ? step_delay :
4596                         euclid(max_step_delay, step_delay));
4597       num_steps[nr] = move_steps;
4598
4599       if (!is_panel)
4600       {
4601         max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4602
4603         panel_has_doors[door_index] = TRUE;
4604       }
4605     }
4606
4607     max_step_delay = MAX(1, max_step_delay);    // prevent division by zero
4608
4609     num_move_steps = max_move_delay / max_step_delay;
4610     num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4611
4612     door_delay_value = max_step_delay;
4613
4614     if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4615     {
4616       start = num_move_steps - 1;
4617     }
4618     else
4619     {
4620       /* opening door sound has priority over simultaneously closing door */
4621       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4622         PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4623       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4624         PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4625     }
4626
4627     for (k = start; k < num_move_steps; k++)
4628     {
4629       int last_frame = num_move_steps - 1;      // last frame of this "for" loop
4630
4631       door_part_done_all = TRUE;
4632
4633       for (i = 0; i < NUM_DOORS; i++)
4634         door_panel_drawn[i] = FALSE;
4635
4636       for (i = 0; i < MAX_DOOR_PARTS; i++)
4637       {
4638         int nr = door_part_order[i].nr;
4639         struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4640         struct DoorPartPosInfo *pos = dpc->pos;
4641         struct GraphicInfo *g = &graphic_info[dpc->graphic];
4642         int door_token = dpc->door_token;
4643         int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4644         boolean is_panel = DOOR_PART_IS_PANEL(nr);
4645         boolean is_panel_and_door_has_closed = FALSE;
4646         struct Rect *door_rect = &door_rect_list[door_index];
4647         Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4648                                   bitmap_db_door_2);
4649         Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4650         int current_door_state = door_state & door_token;
4651         boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
4652         boolean door_closing = !door_opening;
4653         boolean part_opening = (is_panel ? door_closing : door_opening);
4654         boolean part_closing = !part_opening;
4655         int start_step = (part_opening ? pos->start_step_opening :
4656                           pos->start_step_closing);
4657         int step_delay = pos->step_delay;
4658         int step_factor = step_delay / max_step_delay;
4659         int k1 = (step_factor ? k / step_factor + 1 : k);
4660         int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4661         int kk = MAX(0, k2);
4662         int g_src_x = 0;
4663         int g_src_y = 0;
4664         int src_x, src_y, src_xx, src_yy;
4665         int dst_x, dst_y, dst_xx, dst_yy;
4666         int width, height;
4667
4668         if (door_part_skip[nr])
4669           continue;
4670
4671         if (!(door_state & door_token))
4672           continue;
4673
4674         if (!g->bitmap)
4675           continue;
4676
4677         if (!is_panel)
4678         {
4679           int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4680           int kk_door = MAX(0, k2_door);
4681           int sync_frame = kk_door * door_delay_value;
4682           int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4683
4684           getFixedGraphicSource(dpc->graphic, frame, &bitmap,
4685                                 &g_src_x, &g_src_y);
4686         }
4687
4688         // draw door panel
4689
4690         if (!door_panel_drawn[door_index])
4691         {
4692           ClearRectangle(drawto, door_rect->x, door_rect->y,
4693                          door_rect->width, door_rect->height);
4694
4695           door_panel_drawn[door_index] = TRUE;
4696         }
4697
4698         // draw opening or closing door parts
4699
4700         if (pos->step_xoffset < 0)      // door part on right side
4701         {
4702           src_xx = 0;
4703           dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4704           width = g->width;
4705
4706           if (dst_xx + width > door_rect->width)
4707             width = door_rect->width - dst_xx;
4708         }
4709         else                            // door part on left side
4710         {
4711           src_xx = 0;
4712           dst_xx = pos->x - kk * pos->step_xoffset;
4713
4714           if (dst_xx < 0)
4715           {
4716             src_xx = ABS(dst_xx);
4717             dst_xx = 0;
4718           }
4719
4720           width = g->width - src_xx;
4721
4722           if (width > door_rect->width)
4723             width = door_rect->width;
4724
4725           // printf("::: k == %d [%d] \n", k, start_step);
4726         }
4727
4728         if (pos->step_yoffset < 0)      // door part on bottom side
4729         {
4730           src_yy = 0;
4731           dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4732           height = g->height;
4733
4734           if (dst_yy + height > door_rect->height)
4735             height = door_rect->height - dst_yy;
4736         }
4737         else                            // door part on top side
4738         {
4739           src_yy = 0;
4740           dst_yy = pos->y - kk * pos->step_yoffset;
4741
4742           if (dst_yy < 0)
4743           {
4744             src_yy = ABS(dst_yy);
4745             dst_yy = 0;
4746           }
4747
4748           height = g->height - src_yy;
4749         }
4750
4751         src_x = g_src_x + src_xx;
4752         src_y = g_src_y + src_yy;
4753
4754         dst_x = door_rect->x + dst_xx;
4755         dst_y = door_rect->y + dst_yy;
4756
4757         is_panel_and_door_has_closed =
4758           (is_panel &&
4759            door_closing &&
4760            panel_has_doors[door_index] &&
4761            k >= num_move_steps_doors_only - 1);
4762
4763         if (width  >= 0 && width  <= g->width &&
4764             height >= 0 && height <= g->height &&
4765             !is_panel_and_door_has_closed)
4766         {
4767           if (is_panel || !pos->draw_masked)
4768             BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4769                        dst_x, dst_y);
4770           else
4771             BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4772                              dst_x, dst_y);
4773         }
4774
4775         redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4776
4777         if ((part_opening && (width < 0         || height < 0)) ||
4778             (part_closing && (width >= g->width && height >= g->height)))
4779           door_part_done[nr] = TRUE;
4780
4781         // continue door part animations, but not panel after door has closed
4782         if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4783           door_part_done_all = FALSE;
4784       }
4785
4786       if (!(door_state & DOOR_NO_DELAY))
4787       {
4788         BackToFront();
4789
4790         SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4791
4792         current_move_delay += max_step_delay;
4793       }
4794
4795       if (door_part_done_all)
4796         break;
4797     }
4798   }
4799
4800   if (door_state & DOOR_ACTION_1)
4801     door1 = door_state & DOOR_ACTION_1;
4802   if (door_state & DOOR_ACTION_2)
4803     door2 = door_state & DOOR_ACTION_2;
4804
4805   // draw masked border over door area
4806   DrawMaskedBorder(REDRAW_DOOR_1);
4807   DrawMaskedBorder(REDRAW_DOOR_2);
4808
4809   return (door1 | door2);
4810 }
4811
4812 static boolean useSpecialEditorDoor()
4813 {
4814   int graphic = IMG_GLOBAL_BORDER_EDITOR;
4815   boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4816
4817   // do not draw special editor door if editor border defined or redefined
4818   if (graphic_info[graphic].bitmap != NULL || redefined)
4819     return FALSE;
4820
4821   // do not draw special editor door if global border defined to be empty
4822   if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4823     return FALSE;
4824
4825   // do not draw special editor door if viewport definitions do not match
4826   if (EX != VX ||
4827       EY >= VY ||
4828       EXSIZE != VXSIZE ||
4829       EY + EYSIZE != VY + VYSIZE)
4830     return FALSE;
4831
4832   return TRUE;
4833 }
4834
4835 void DrawSpecialEditorDoor()
4836 {
4837   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4838   int top_border_width = gfx1->width;
4839   int top_border_height = gfx1->height;
4840   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4841   int ex = EX - outer_border;
4842   int ey = EY - outer_border;
4843   int vy = VY - outer_border;
4844   int exsize = EXSIZE + 2 * outer_border;
4845
4846   if (!useSpecialEditorDoor())
4847     return;
4848
4849   /* draw bigger level editor toolbox window */
4850   BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4851              top_border_width, top_border_height, ex, ey - top_border_height);
4852   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4853              exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4854
4855   redraw_mask |= REDRAW_ALL;
4856 }
4857
4858 void UndrawSpecialEditorDoor()
4859 {
4860   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4861   int top_border_width = gfx1->width;
4862   int top_border_height = gfx1->height;
4863   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4864   int ex = EX - outer_border;
4865   int ey = EY - outer_border;
4866   int ey_top = ey - top_border_height;
4867   int exsize = EXSIZE + 2 * outer_border;
4868   int eysize = EYSIZE + 2 * outer_border;
4869
4870   if (!useSpecialEditorDoor())
4871     return;
4872
4873   /* draw normal tape recorder window */
4874   if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4875   {
4876     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4877                ex, ey_top, top_border_width, top_border_height,
4878                ex, ey_top);
4879     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4880                ex, ey, exsize, eysize, ex, ey);
4881   }
4882   else
4883   {
4884     // if screen background is set to "[NONE]", clear editor toolbox window
4885     ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4886     ClearRectangle(drawto, ex, ey, exsize, eysize);
4887   }
4888
4889   redraw_mask |= REDRAW_ALL;
4890 }
4891
4892
4893 /* ---------- new tool button stuff ---------------------------------------- */
4894
4895 static struct
4896 {
4897   int graphic;
4898   struct TextPosInfo *pos;
4899   int gadget_id;
4900   char *infotext;
4901 } toolbutton_info[NUM_TOOL_BUTTONS] =
4902 {
4903   {
4904     IMG_GFX_REQUEST_BUTTON_YES,         &request.button.yes,
4905     TOOL_CTRL_ID_YES,                   "yes"
4906   },
4907   {
4908     IMG_GFX_REQUEST_BUTTON_NO,          &request.button.no,
4909     TOOL_CTRL_ID_NO,                    "no"
4910   },
4911   {
4912     IMG_GFX_REQUEST_BUTTON_CONFIRM,     &request.button.confirm,
4913     TOOL_CTRL_ID_CONFIRM,               "confirm"
4914   },
4915   {
4916     IMG_GFX_REQUEST_BUTTON_PLAYER_1,    &request.button.player_1,
4917     TOOL_CTRL_ID_PLAYER_1,              "player 1"
4918   },
4919   {
4920     IMG_GFX_REQUEST_BUTTON_PLAYER_2,    &request.button.player_2,
4921     TOOL_CTRL_ID_PLAYER_2,              "player 2"
4922   },
4923   {
4924     IMG_GFX_REQUEST_BUTTON_PLAYER_3,    &request.button.player_3,
4925     TOOL_CTRL_ID_PLAYER_3,              "player 3"
4926   },
4927   {
4928     IMG_GFX_REQUEST_BUTTON_PLAYER_4,    &request.button.player_4,
4929     TOOL_CTRL_ID_PLAYER_4,              "player 4"
4930   }
4931 };
4932
4933 void CreateToolButtons()
4934 {
4935   int i;
4936
4937   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4938   {
4939     struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4940     struct TextPosInfo *pos = toolbutton_info[i].pos;
4941     struct GadgetInfo *gi;
4942     Bitmap *deco_bitmap = None;
4943     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4944     unsigned int event_mask = GD_EVENT_RELEASED;
4945     int dx = DX;
4946     int dy = DY;
4947     int gd_x = gfx->src_x;
4948     int gd_y = gfx->src_y;
4949     int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4950     int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4951     int id = i;
4952
4953     if (global.use_envelope_request)
4954       setRequestPosition(&dx, &dy, TRUE);
4955
4956     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4957     {
4958       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4959
4960       getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4961                             pos->size, &deco_bitmap, &deco_x, &deco_y);
4962       deco_xpos = (gfx->width  - pos->size) / 2;
4963       deco_ypos = (gfx->height - pos->size) / 2;
4964     }
4965
4966     gi = CreateGadget(GDI_CUSTOM_ID, id,
4967                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
4968                       GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4969                       GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4970                       GDI_WIDTH, gfx->width,
4971                       GDI_HEIGHT, gfx->height,
4972                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4973                       GDI_STATE, GD_BUTTON_UNPRESSED,
4974                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4975                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4976                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4977                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4978                       GDI_DECORATION_SIZE, pos->size, pos->size,
4979                       GDI_DECORATION_SHIFTING, 1, 1,
4980                       GDI_DIRECT_DRAW, FALSE,
4981                       GDI_EVENT_MASK, event_mask,
4982                       GDI_CALLBACK_ACTION, HandleToolButtons,
4983                       GDI_END);
4984
4985     if (gi == NULL)
4986       Error(ERR_EXIT, "cannot create gadget");
4987
4988     tool_gadget[id] = gi;
4989   }
4990 }
4991
4992 void FreeToolButtons()
4993 {
4994   int i;
4995
4996   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4997     FreeGadget(tool_gadget[i]);
4998 }
4999
5000 static void UnmapToolButtons()
5001 {
5002   int i;
5003
5004   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5005     UnmapGadget(tool_gadget[i]);
5006 }
5007
5008 static void HandleToolButtons(struct GadgetInfo *gi)
5009 {
5010   request_gadget_id = gi->custom_id;
5011 }
5012
5013 static struct Mapping_EM_to_RND_object
5014 {
5015   int element_em;
5016   boolean is_rnd_to_em_mapping;         /* unique mapping EM <-> RND */
5017   boolean is_backside;                  /* backside of moving element */
5018
5019   int element_rnd;
5020   int action;
5021   int direction;
5022 }
5023 em_object_mapping_list[] =
5024 {
5025   {
5026     Xblank,                             TRUE,   FALSE,
5027     EL_EMPTY,                           -1, -1
5028   },
5029   {
5030     Yacid_splash_eB,                    FALSE,  FALSE,
5031     EL_ACID_SPLASH_RIGHT,               -1, -1
5032   },
5033   {
5034     Yacid_splash_wB,                    FALSE,  FALSE,
5035     EL_ACID_SPLASH_LEFT,                -1, -1
5036   },
5037
5038 #ifdef EM_ENGINE_BAD_ROLL
5039   {
5040     Xstone_force_e,                     FALSE,  FALSE,
5041     EL_ROCK,                            -1, MV_BIT_RIGHT
5042   },
5043   {
5044     Xstone_force_w,                     FALSE,  FALSE,
5045     EL_ROCK,                            -1, MV_BIT_LEFT
5046   },
5047   {
5048     Xnut_force_e,                       FALSE,  FALSE,
5049     EL_NUT,                             -1, MV_BIT_RIGHT
5050   },
5051   {
5052     Xnut_force_w,                       FALSE,  FALSE,
5053     EL_NUT,                             -1, MV_BIT_LEFT
5054   },
5055   {
5056     Xspring_force_e,                    FALSE,  FALSE,
5057     EL_SPRING,                          -1, MV_BIT_RIGHT
5058   },
5059   {
5060     Xspring_force_w,                    FALSE,  FALSE,
5061     EL_SPRING,                          -1, MV_BIT_LEFT
5062   },
5063   {
5064     Xemerald_force_e,                   FALSE,  FALSE,
5065     EL_EMERALD,                         -1, MV_BIT_RIGHT
5066   },
5067   {
5068     Xemerald_force_w,                   FALSE,  FALSE,
5069     EL_EMERALD,                         -1, MV_BIT_LEFT
5070   },
5071   {
5072     Xdiamond_force_e,                   FALSE,  FALSE,
5073     EL_DIAMOND,                         -1, MV_BIT_RIGHT
5074   },
5075   {
5076     Xdiamond_force_w,                   FALSE,  FALSE,
5077     EL_DIAMOND,                         -1, MV_BIT_LEFT
5078   },
5079   {
5080     Xbomb_force_e,                      FALSE,  FALSE,
5081     EL_BOMB,                            -1, MV_BIT_RIGHT
5082   },
5083   {
5084     Xbomb_force_w,                      FALSE,  FALSE,
5085     EL_BOMB,                            -1, MV_BIT_LEFT
5086   },
5087 #endif  /* EM_ENGINE_BAD_ROLL */
5088
5089   {
5090     Xstone,                             TRUE,   FALSE,
5091     EL_ROCK,                            -1, -1
5092   },
5093   {
5094     Xstone_pause,                       FALSE,  FALSE,
5095     EL_ROCK,                            -1, -1
5096   },
5097   {
5098     Xstone_fall,                        FALSE,  FALSE,
5099     EL_ROCK,                            -1, -1
5100   },
5101   {
5102     Ystone_s,                           FALSE,  FALSE,
5103     EL_ROCK,                            ACTION_FALLING, -1
5104   },
5105   {
5106     Ystone_sB,                          FALSE,  TRUE,
5107     EL_ROCK,                            ACTION_FALLING, -1
5108   },
5109   {
5110     Ystone_e,                           FALSE,  FALSE,
5111     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
5112   },
5113   {
5114     Ystone_eB,                          FALSE,  TRUE,
5115     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
5116   },
5117   {
5118     Ystone_w,                           FALSE,  FALSE,
5119     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
5120   },
5121   {
5122     Ystone_wB,                          FALSE,  TRUE,
5123     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
5124   },
5125   {
5126     Xnut,                               TRUE,   FALSE,
5127     EL_NUT,                             -1, -1
5128   },
5129   {
5130     Xnut_pause,                         FALSE,  FALSE,
5131     EL_NUT,                             -1, -1
5132   },
5133   {
5134     Xnut_fall,                          FALSE,  FALSE,
5135     EL_NUT,                             -1, -1
5136   },
5137   {
5138     Ynut_s,                             FALSE,  FALSE,
5139     EL_NUT,                             ACTION_FALLING, -1
5140   },
5141   {
5142     Ynut_sB,                            FALSE,  TRUE,
5143     EL_NUT,                             ACTION_FALLING, -1
5144   },
5145   {
5146     Ynut_e,                             FALSE,  FALSE,
5147     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
5148   },
5149   {
5150     Ynut_eB,                            FALSE,  TRUE,
5151     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
5152   },
5153   {
5154     Ynut_w,                             FALSE,  FALSE,
5155     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
5156   },
5157   {
5158     Ynut_wB,                            FALSE,  TRUE,
5159     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
5160   },
5161   {
5162     Xbug_n,                             TRUE,   FALSE,
5163     EL_BUG_UP,                          -1, -1
5164   },
5165   {
5166     Xbug_e,                             TRUE,   FALSE,
5167     EL_BUG_RIGHT,                       -1, -1
5168   },
5169   {
5170     Xbug_s,                             TRUE,   FALSE,
5171     EL_BUG_DOWN,                        -1, -1
5172   },
5173   {
5174     Xbug_w,                             TRUE,   FALSE,
5175     EL_BUG_LEFT,                        -1, -1
5176   },
5177   {
5178     Xbug_gon,                           FALSE,  FALSE,
5179     EL_BUG_UP,                          -1, -1
5180   },
5181   {
5182     Xbug_goe,                           FALSE,  FALSE,
5183     EL_BUG_RIGHT,                       -1, -1
5184   },
5185   {
5186     Xbug_gos,                           FALSE,  FALSE,
5187     EL_BUG_DOWN,                        -1, -1
5188   },
5189   {
5190     Xbug_gow,                           FALSE,  FALSE,
5191     EL_BUG_LEFT,                        -1, -1
5192   },
5193   {
5194     Ybug_n,                             FALSE,  FALSE,
5195     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
5196   },
5197   {
5198     Ybug_nB,                            FALSE,  TRUE,
5199     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
5200   },
5201   {
5202     Ybug_e,                             FALSE,  FALSE,
5203     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
5204   },
5205   {
5206     Ybug_eB,                            FALSE,  TRUE,
5207     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
5208   },
5209   {
5210     Ybug_s,                             FALSE,  FALSE,
5211     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
5212   },
5213   {
5214     Ybug_sB,                            FALSE,  TRUE,
5215     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
5216   },
5217   {
5218     Ybug_w,                             FALSE,  FALSE,
5219     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
5220   },
5221   {
5222     Ybug_wB,                            FALSE,  TRUE,
5223     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
5224   },
5225   {
5226     Ybug_w_n,                           FALSE,  FALSE,
5227     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5228   },
5229   {
5230     Ybug_n_e,                           FALSE,  FALSE,
5231     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5232   },
5233   {
5234     Ybug_e_s,                           FALSE,  FALSE,
5235     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5236   },
5237   {
5238     Ybug_s_w,                           FALSE,  FALSE,
5239     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5240   },
5241   {
5242     Ybug_e_n,                           FALSE,  FALSE,
5243     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5244   },
5245   {
5246     Ybug_s_e,                           FALSE,  FALSE,
5247     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5248   },
5249   {
5250     Ybug_w_s,                           FALSE,  FALSE,
5251     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5252   },
5253   {
5254     Ybug_n_w,                           FALSE,  FALSE,
5255     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5256   },
5257   {
5258     Ybug_stone,                         FALSE,  FALSE,
5259     EL_BUG,                             ACTION_SMASHED_BY_ROCK, -1
5260   },
5261   {
5262     Ybug_spring,                        FALSE,  FALSE,
5263     EL_BUG,                             ACTION_SMASHED_BY_SPRING, -1
5264   },
5265   {
5266     Xtank_n,                            TRUE,   FALSE,
5267     EL_SPACESHIP_UP,                    -1, -1
5268   },
5269   {
5270     Xtank_e,                            TRUE,   FALSE,
5271     EL_SPACESHIP_RIGHT,                 -1, -1
5272   },
5273   {
5274     Xtank_s,                            TRUE,   FALSE,
5275     EL_SPACESHIP_DOWN,                  -1, -1
5276   },
5277   {
5278     Xtank_w,                            TRUE,   FALSE,
5279     EL_SPACESHIP_LEFT,                  -1, -1
5280   },
5281   {
5282     Xtank_gon,                          FALSE,  FALSE,
5283     EL_SPACESHIP_UP,                    -1, -1
5284   },
5285   {
5286     Xtank_goe,                          FALSE,  FALSE,
5287     EL_SPACESHIP_RIGHT,                 -1, -1
5288   },
5289   {
5290     Xtank_gos,                          FALSE,  FALSE,
5291     EL_SPACESHIP_DOWN,                  -1, -1
5292   },
5293   {
5294     Xtank_gow,                          FALSE,  FALSE,
5295     EL_SPACESHIP_LEFT,                  -1, -1
5296   },
5297   {
5298     Ytank_n,                            FALSE,  FALSE,
5299     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
5300   },
5301   {
5302     Ytank_nB,                           FALSE,  TRUE,
5303     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
5304   },
5305   {
5306     Ytank_e,                            FALSE,  FALSE,
5307     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
5308   },
5309   {
5310     Ytank_eB,                           FALSE,  TRUE,
5311     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
5312   },
5313   {
5314     Ytank_s,                            FALSE,  FALSE,
5315     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
5316   },
5317   {
5318     Ytank_sB,                           FALSE,  TRUE,
5319     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
5320   },
5321   {
5322     Ytank_w,                            FALSE,  FALSE,
5323     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
5324   },
5325   {
5326     Ytank_wB,                           FALSE,  TRUE,
5327     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
5328   },
5329   {
5330     Ytank_w_n,                          FALSE,  FALSE,
5331     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5332   },
5333   {
5334     Ytank_n_e,                          FALSE,  FALSE,
5335     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5336   },
5337   {
5338     Ytank_e_s,                          FALSE,  FALSE,
5339     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5340   },
5341   {
5342     Ytank_s_w,                          FALSE,  FALSE,
5343     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5344   },
5345   {
5346     Ytank_e_n,                          FALSE,  FALSE,
5347     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5348   },
5349   {
5350     Ytank_s_e,                          FALSE,  FALSE,
5351     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5352   },
5353   {
5354     Ytank_w_s,                          FALSE,  FALSE,
5355     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5356   },
5357   {
5358     Ytank_n_w,                          FALSE,  FALSE,
5359     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5360   },
5361   {
5362     Ytank_stone,                        FALSE,  FALSE,
5363     EL_SPACESHIP,                       ACTION_SMASHED_BY_ROCK, -1
5364   },
5365   {
5366     Ytank_spring,                       FALSE,  FALSE,
5367     EL_SPACESHIP,                       ACTION_SMASHED_BY_SPRING, -1
5368   },
5369   {
5370     Xandroid,                           TRUE,   FALSE,
5371     EL_EMC_ANDROID,                     ACTION_ACTIVE, -1
5372   },
5373   {
5374     Xandroid_1_n,                       FALSE,  FALSE,
5375     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
5376   },
5377   {
5378     Xandroid_2_n,                       FALSE,  FALSE,
5379     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
5380   },
5381   {
5382     Xandroid_1_e,                       FALSE,  FALSE,
5383     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
5384   },
5385   {
5386     Xandroid_2_e,                       FALSE,  FALSE,
5387     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
5388   },
5389   {
5390     Xandroid_1_w,                       FALSE,  FALSE,
5391     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
5392   },
5393   {
5394     Xandroid_2_w,                       FALSE,  FALSE,
5395     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
5396   },
5397   {
5398     Xandroid_1_s,                       FALSE,  FALSE,
5399     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
5400   },
5401   {
5402     Xandroid_2_s,                       FALSE,  FALSE,
5403     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
5404   },
5405   {
5406     Yandroid_n,                         FALSE,  FALSE,
5407     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
5408   },
5409   {
5410     Yandroid_nB,                        FALSE,  TRUE,
5411     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
5412   },
5413   {
5414     Yandroid_ne,                        FALSE,  FALSE,
5415     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPRIGHT
5416   },
5417   {
5418     Yandroid_neB,                       FALSE,  TRUE,
5419     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPRIGHT
5420   },
5421   {
5422     Yandroid_e,                         FALSE,  FALSE,
5423     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
5424   },
5425   {
5426     Yandroid_eB,                        FALSE,  TRUE,
5427     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
5428   },
5429   {
5430     Yandroid_se,                        FALSE,  FALSE,
5431     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNRIGHT
5432   },
5433   {
5434     Yandroid_seB,                       FALSE,  TRUE,
5435     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5436   },
5437   {
5438     Yandroid_s,                         FALSE,  FALSE,
5439     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
5440   },
5441   {
5442     Yandroid_sB,                        FALSE,  TRUE,
5443     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
5444   },
5445   {
5446     Yandroid_sw,                        FALSE,  FALSE,
5447     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNLEFT
5448   },
5449   {
5450     Yandroid_swB,                       FALSE,  TRUE,
5451     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNLEFT
5452   },
5453   {
5454     Yandroid_w,                         FALSE,  FALSE,
5455     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
5456   },
5457   {
5458     Yandroid_wB,                        FALSE,  TRUE,
5459     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
5460   },
5461   {
5462     Yandroid_nw,                        FALSE,  FALSE,
5463     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPLEFT
5464   },
5465   {
5466     Yandroid_nwB,                       FALSE,  TRUE,
5467     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPLEFT
5468   },
5469   {
5470     Xspring,                            TRUE,   FALSE,
5471     EL_SPRING,                          -1, -1
5472   },
5473   {
5474     Xspring_pause,                      FALSE,  FALSE,
5475     EL_SPRING,                          -1, -1
5476   },
5477   {
5478     Xspring_e,                          FALSE,  FALSE,
5479     EL_SPRING,                          -1, -1
5480   },
5481   {
5482     Xspring_w,                          FALSE,  FALSE,
5483     EL_SPRING,                          -1, -1
5484   },
5485   {
5486     Xspring_fall,                       FALSE,  FALSE,
5487     EL_SPRING,                          -1, -1
5488   },
5489   {
5490     Yspring_s,                          FALSE,  FALSE,
5491     EL_SPRING,                          ACTION_FALLING, -1
5492   },
5493   {
5494     Yspring_sB,                         FALSE,  TRUE,
5495     EL_SPRING,                          ACTION_FALLING, -1
5496   },
5497   {
5498     Yspring_e,                          FALSE,  FALSE,
5499     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
5500   },
5501   {
5502     Yspring_eB,                         FALSE,  TRUE,
5503     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
5504   },
5505   {
5506     Yspring_w,                          FALSE,  FALSE,
5507     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
5508   },
5509   {
5510     Yspring_wB,                         FALSE,  TRUE,
5511     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
5512   },
5513   {
5514     Yspring_kill_e,                     FALSE,  FALSE,
5515     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
5516   },
5517   {
5518     Yspring_kill_eB,                    FALSE,  TRUE,
5519     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
5520   },
5521   {
5522     Yspring_kill_w,                     FALSE,  FALSE,
5523     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
5524   },
5525   {
5526     Yspring_kill_wB,                    FALSE,  TRUE,
5527     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
5528   },
5529   {
5530     Xeater_n,                           TRUE,   FALSE,
5531     EL_YAMYAM_UP,                       -1, -1
5532   },
5533   {
5534     Xeater_e,                           TRUE,   FALSE,
5535     EL_YAMYAM_RIGHT,                    -1, -1
5536   },
5537   {
5538     Xeater_w,                           TRUE,   FALSE,
5539     EL_YAMYAM_LEFT,                     -1, -1
5540   },
5541   {
5542     Xeater_s,                           TRUE,   FALSE,
5543     EL_YAMYAM_DOWN,                     -1, -1
5544   },
5545   {
5546     Yeater_n,                           FALSE,  FALSE,
5547     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
5548   },
5549   {
5550     Yeater_nB,                          FALSE,  TRUE,
5551     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
5552   },
5553   {
5554     Yeater_e,                           FALSE,  FALSE,
5555     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
5556   },
5557   {
5558     Yeater_eB,                          FALSE,  TRUE,
5559     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
5560   },
5561   {
5562     Yeater_s,                           FALSE,  FALSE,
5563     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
5564   },
5565   {
5566     Yeater_sB,                          FALSE,  TRUE,
5567     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
5568   },
5569   {
5570     Yeater_w,                           FALSE,  FALSE,
5571     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
5572   },
5573   {
5574     Yeater_wB,                          FALSE,  TRUE,
5575     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
5576   },
5577   {
5578     Yeater_stone,                       FALSE,  FALSE,
5579     EL_YAMYAM,                          ACTION_SMASHED_BY_ROCK, -1
5580   },
5581   {
5582     Yeater_spring,                      FALSE,  FALSE,
5583     EL_YAMYAM,                          ACTION_SMASHED_BY_SPRING, -1
5584   },
5585   {
5586     Xalien,                             TRUE,   FALSE,
5587     EL_ROBOT,                           -1, -1
5588   },
5589   {
5590     Xalien_pause,                       FALSE,  FALSE,
5591     EL_ROBOT,                           -1, -1
5592   },
5593   {
5594     Yalien_n,                           FALSE,  FALSE,
5595     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
5596   },
5597   {
5598     Yalien_nB,                          FALSE,  TRUE,
5599     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
5600   },
5601   {
5602     Yalien_e,                           FALSE,  FALSE,
5603     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
5604   },
5605   {
5606     Yalien_eB,                          FALSE,  TRUE,
5607     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
5608   },
5609   {
5610     Yalien_s,                           FALSE,  FALSE,
5611     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
5612   },
5613   {
5614     Yalien_sB,                          FALSE,  TRUE,
5615     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
5616   },
5617   {
5618     Yalien_w,                           FALSE,  FALSE,
5619     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
5620   },
5621   {
5622     Yalien_wB,                          FALSE,  TRUE,
5623     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
5624   },
5625   {
5626     Yalien_stone,                       FALSE,  FALSE,
5627     EL_ROBOT,                           ACTION_SMASHED_BY_ROCK, -1
5628   },
5629   {
5630     Yalien_spring,                      FALSE,  FALSE,
5631     EL_ROBOT,                           ACTION_SMASHED_BY_SPRING, -1
5632   },
5633   {
5634     Xemerald,                           TRUE,   FALSE,
5635     EL_EMERALD,                         -1, -1
5636   },
5637   {
5638     Xemerald_pause,                     FALSE,  FALSE,
5639     EL_EMERALD,                         -1, -1
5640   },
5641   {
5642     Xemerald_fall,                      FALSE,  FALSE,
5643     EL_EMERALD,                         -1, -1
5644   },
5645   {
5646     Xemerald_shine,                     FALSE,  FALSE,
5647     EL_EMERALD,                         ACTION_TWINKLING, -1
5648   },
5649   {
5650     Yemerald_s,                         FALSE,  FALSE,
5651     EL_EMERALD,                         ACTION_FALLING, -1
5652   },
5653   {
5654     Yemerald_sB,                        FALSE,  TRUE,
5655     EL_EMERALD,                         ACTION_FALLING, -1
5656   },
5657   {
5658     Yemerald_e,                         FALSE,  FALSE,
5659     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
5660   },
5661   {
5662     Yemerald_eB,                        FALSE,  TRUE,
5663     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
5664   },
5665   {
5666     Yemerald_w,                         FALSE,  FALSE,
5667     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
5668   },
5669   {
5670     Yemerald_wB,                        FALSE,  TRUE,
5671     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
5672   },
5673   {
5674     Yemerald_eat,                       FALSE,  FALSE,
5675     EL_EMERALD,                         ACTION_COLLECTING, -1
5676   },
5677   {
5678     Yemerald_stone,                     FALSE,  FALSE,
5679     EL_NUT,                             ACTION_BREAKING, -1
5680   },
5681   {
5682     Xdiamond,                           TRUE,   FALSE,
5683     EL_DIAMOND,                         -1, -1
5684   },
5685   {
5686     Xdiamond_pause,                     FALSE,  FALSE,
5687     EL_DIAMOND,                         -1, -1
5688   },
5689   {
5690     Xdiamond_fall,                      FALSE,  FALSE,
5691     EL_DIAMOND,                         -1, -1
5692   },
5693   {
5694     Xdiamond_shine,                     FALSE,  FALSE,
5695     EL_DIAMOND,                         ACTION_TWINKLING, -1
5696   },
5697   {
5698     Ydiamond_s,                         FALSE,  FALSE,
5699     EL_DIAMOND,                         ACTION_FALLING, -1
5700   },
5701   {
5702     Ydiamond_sB,                        FALSE,  TRUE,
5703     EL_DIAMOND,                         ACTION_FALLING, -1
5704   },
5705   {
5706     Ydiamond_e,                         FALSE,  FALSE,
5707     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
5708   },
5709   {
5710     Ydiamond_eB,                        FALSE,  TRUE,
5711     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
5712   },
5713   {
5714     Ydiamond_w,                         FALSE,  FALSE,
5715     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
5716   },
5717   {
5718     Ydiamond_wB,                        FALSE,  TRUE,
5719     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
5720   },
5721   {
5722     Ydiamond_eat,                       FALSE,  FALSE,
5723     EL_DIAMOND,                         ACTION_COLLECTING, -1
5724   },
5725   {
5726     Ydiamond_stone,                     FALSE,  FALSE,
5727     EL_DIAMOND,                         ACTION_SMASHED_BY_ROCK, -1
5728   },
5729   {
5730     Xdrip_fall,                         TRUE,   FALSE,
5731     EL_AMOEBA_DROP,                     -1, -1
5732   },
5733   {
5734     Xdrip_stretch,                      FALSE,  FALSE,
5735     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5736   },
5737   {
5738     Xdrip_stretchB,                     FALSE,  TRUE,
5739     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5740   },
5741   {
5742     Xdrip_eat,                          FALSE,  FALSE,
5743     EL_AMOEBA_DROP,                     ACTION_GROWING, -1
5744   },
5745   {
5746     Ydrip_s1,                           FALSE,  FALSE,
5747     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5748   },
5749   {
5750     Ydrip_s1B,                          FALSE,  TRUE,
5751     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5752   },
5753   {
5754     Ydrip_s2,                           FALSE,  FALSE,
5755     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5756   },
5757   {
5758     Ydrip_s2B,                          FALSE,  TRUE,
5759     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5760   },
5761   {
5762     Xbomb,                              TRUE,   FALSE,
5763     EL_BOMB,                            -1, -1
5764   },
5765   {
5766     Xbomb_pause,                        FALSE,  FALSE,
5767     EL_BOMB,                            -1, -1
5768   },
5769   {
5770     Xbomb_fall,                         FALSE,  FALSE,
5771     EL_BOMB,                            -1, -1
5772   },
5773   {
5774     Ybomb_s,                            FALSE,  FALSE,
5775     EL_BOMB,                            ACTION_FALLING, -1
5776   },
5777   {
5778     Ybomb_sB,                           FALSE,  TRUE,
5779     EL_BOMB,                            ACTION_FALLING, -1
5780   },
5781   {
5782     Ybomb_e,                            FALSE,  FALSE,
5783     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
5784   },
5785   {
5786     Ybomb_eB,                           FALSE,  TRUE,
5787     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
5788   },
5789   {
5790     Ybomb_w,                            FALSE,  FALSE,
5791     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
5792   },
5793   {
5794     Ybomb_wB,                           FALSE,  TRUE,
5795     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
5796   },
5797   {
5798     Ybomb_eat,                          FALSE,  FALSE,
5799     EL_BOMB,                            ACTION_ACTIVATING, -1
5800   },
5801   {
5802     Xballoon,                           TRUE,   FALSE,
5803     EL_BALLOON,                         -1, -1
5804   },
5805   {
5806     Yballoon_n,                         FALSE,  FALSE,
5807     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
5808   },
5809   {
5810     Yballoon_nB,                        FALSE,  TRUE,
5811     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
5812   },
5813   {
5814     Yballoon_e,                         FALSE,  FALSE,
5815     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
5816   },
5817   {
5818     Yballoon_eB,                        FALSE,  TRUE,
5819     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
5820   },
5821   {
5822     Yballoon_s,                         FALSE,  FALSE,
5823     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
5824   },
5825   {
5826     Yballoon_sB,                        FALSE,  TRUE,
5827     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
5828   },
5829   {
5830     Yballoon_w,                         FALSE,  FALSE,
5831     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
5832   },
5833   {
5834     Yballoon_wB,                        FALSE,  TRUE,
5835     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
5836   },
5837   {
5838     Xgrass,                             TRUE,   FALSE,
5839     EL_EMC_GRASS,                       -1, -1
5840   },
5841   {
5842     Ygrass_nB,                          FALSE,  FALSE,
5843     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_UP
5844   },
5845   {
5846     Ygrass_eB,                          FALSE,  FALSE,
5847     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_RIGHT
5848   },
5849   {
5850     Ygrass_sB,                          FALSE,  FALSE,
5851     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_DOWN
5852   },
5853   {
5854     Ygrass_wB,                          FALSE,  FALSE,
5855     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_LEFT
5856   },
5857   {
5858     Xdirt,                              TRUE,   FALSE,
5859     EL_SAND,                            -1, -1
5860   },
5861   {
5862     Ydirt_nB,                           FALSE,  FALSE,
5863     EL_SAND,                            ACTION_DIGGING, MV_BIT_UP
5864   },
5865   {
5866     Ydirt_eB,                           FALSE,  FALSE,
5867     EL_SAND,                            ACTION_DIGGING, MV_BIT_RIGHT
5868   },
5869   {
5870     Ydirt_sB,                           FALSE,  FALSE,
5871     EL_SAND,                            ACTION_DIGGING, MV_BIT_DOWN
5872   },
5873   {
5874     Ydirt_wB,                           FALSE,  FALSE,
5875     EL_SAND,                            ACTION_DIGGING, MV_BIT_LEFT
5876   },
5877   {
5878     Xacid_ne,                           TRUE,   FALSE,
5879     EL_ACID_POOL_TOPRIGHT,              -1, -1
5880   },
5881   {
5882     Xacid_se,                           TRUE,   FALSE,
5883     EL_ACID_POOL_BOTTOMRIGHT,           -1, -1
5884   },
5885   {
5886     Xacid_s,                            TRUE,   FALSE,
5887     EL_ACID_POOL_BOTTOM,                -1, -1
5888   },
5889   {
5890     Xacid_sw,                           TRUE,   FALSE,
5891     EL_ACID_POOL_BOTTOMLEFT,            -1, -1
5892   },
5893   {
5894     Xacid_nw,                           TRUE,   FALSE,
5895     EL_ACID_POOL_TOPLEFT,               -1, -1
5896   },
5897   {
5898     Xacid_1,                            TRUE,   FALSE,
5899     EL_ACID,                            -1, -1
5900   },
5901   {
5902     Xacid_2,                            FALSE,  FALSE,
5903     EL_ACID,                            -1, -1
5904   },
5905   {
5906     Xacid_3,                            FALSE,  FALSE,
5907     EL_ACID,                            -1, -1
5908   },
5909   {
5910     Xacid_4,                            FALSE,  FALSE,
5911     EL_ACID,                            -1, -1
5912   },
5913   {
5914     Xacid_5,                            FALSE,  FALSE,
5915     EL_ACID,                            -1, -1
5916   },
5917   {
5918     Xacid_6,                            FALSE,  FALSE,
5919     EL_ACID,                            -1, -1
5920   },
5921   {
5922     Xacid_7,                            FALSE,  FALSE,
5923     EL_ACID,                            -1, -1
5924   },
5925   {
5926     Xacid_8,                            FALSE,  FALSE,
5927     EL_ACID,                            -1, -1
5928   },
5929   {
5930     Xball_1,                            TRUE,   FALSE,
5931     EL_EMC_MAGIC_BALL,                  -1, -1
5932   },
5933   {
5934     Xball_1B,                           FALSE,  FALSE,
5935     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
5936   },
5937   {
5938     Xball_2,                            FALSE,  FALSE,
5939     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
5940   },
5941   {
5942     Xball_2B,                           FALSE,  FALSE,
5943     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
5944   },
5945   {
5946     Yball_eat,                          FALSE,  FALSE,
5947     EL_EMC_MAGIC_BALL,                  ACTION_DROPPING, -1
5948   },
5949   {
5950     Ykey_1_eat,                         FALSE,  FALSE,
5951     EL_EM_KEY_1,                        ACTION_COLLECTING, -1
5952   },
5953   {
5954     Ykey_2_eat,                         FALSE,  FALSE,
5955     EL_EM_KEY_2,                        ACTION_COLLECTING, -1
5956   },
5957   {
5958     Ykey_3_eat,                         FALSE,  FALSE,
5959     EL_EM_KEY_3,                        ACTION_COLLECTING, -1
5960   },
5961   {
5962     Ykey_4_eat,                         FALSE,  FALSE,
5963     EL_EM_KEY_4,                        ACTION_COLLECTING, -1
5964   },
5965   {
5966     Ykey_5_eat,                         FALSE,  FALSE,
5967     EL_EMC_KEY_5,                       ACTION_COLLECTING, -1
5968   },
5969   {
5970     Ykey_6_eat,                         FALSE,  FALSE,
5971     EL_EMC_KEY_6,                       ACTION_COLLECTING, -1
5972   },
5973   {
5974     Ykey_7_eat,                         FALSE,  FALSE,
5975     EL_EMC_KEY_7,                       ACTION_COLLECTING, -1
5976   },
5977   {
5978     Ykey_8_eat,                         FALSE,  FALSE,
5979     EL_EMC_KEY_8,                       ACTION_COLLECTING, -1
5980   },
5981   {
5982     Ylenses_eat,                        FALSE,  FALSE,
5983     EL_EMC_LENSES,                      ACTION_COLLECTING, -1
5984   },
5985   {
5986     Ymagnify_eat,                       FALSE,  FALSE,
5987     EL_EMC_MAGNIFIER,                   ACTION_COLLECTING, -1
5988   },
5989   {
5990     Ygrass_eat,                         FALSE,  FALSE,
5991     EL_EMC_GRASS,                       ACTION_SNAPPING, -1
5992   },
5993   {
5994     Ydirt_eat,                          FALSE,  FALSE,
5995     EL_SAND,                            ACTION_SNAPPING, -1
5996   },
5997   {
5998     Xgrow_ns,                           TRUE,   FALSE,
5999     EL_EXPANDABLE_WALL_VERTICAL,        -1, -1
6000   },
6001   {
6002     Ygrow_ns_eat,                       FALSE,  FALSE,
6003     EL_EXPANDABLE_WALL_VERTICAL,        ACTION_GROWING, -1
6004   },
6005   {
6006     Xgrow_ew,                           TRUE,   FALSE,
6007     EL_EXPANDABLE_WALL_HORIZONTAL,      -1, -1
6008   },
6009   {
6010     Ygrow_ew_eat,                       FALSE,  FALSE,
6011     EL_EXPANDABLE_WALL_HORIZONTAL,      ACTION_GROWING, -1
6012   },
6013   {
6014     Xwonderwall,                        TRUE,   FALSE,
6015     EL_MAGIC_WALL,                      -1, -1
6016   },
6017   {
6018     XwonderwallB,                       FALSE,  FALSE,
6019     EL_MAGIC_WALL,                      ACTION_ACTIVE, -1
6020   },
6021   {
6022     Xamoeba_1,                          TRUE,   FALSE,
6023     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6024   },
6025   {
6026     Xamoeba_2,                          FALSE,  FALSE,
6027     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6028   },
6029   {
6030     Xamoeba_3,                          FALSE,  FALSE,
6031     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6032   },
6033   {
6034     Xamoeba_4,                          FALSE,  FALSE,
6035     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6036   },
6037   {
6038     Xamoeba_5,                          TRUE,   FALSE,
6039     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6040   },
6041   {
6042     Xamoeba_6,                          FALSE,  FALSE,
6043     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6044   },
6045   {
6046     Xamoeba_7,                          FALSE,  FALSE,
6047     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6048   },
6049   {
6050     Xamoeba_8,                          FALSE,  FALSE,
6051     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6052   },
6053   {
6054     Xdoor_1,                            TRUE,   FALSE,
6055     EL_EM_GATE_1,                       -1, -1
6056   },
6057   {
6058     Xdoor_2,                            TRUE,   FALSE,
6059     EL_EM_GATE_2,                       -1, -1
6060   },
6061   {
6062     Xdoor_3,                            TRUE,   FALSE,
6063     EL_EM_GATE_3,                       -1, -1
6064   },
6065   {
6066     Xdoor_4,                            TRUE,   FALSE,
6067     EL_EM_GATE_4,                       -1, -1
6068   },
6069   {
6070     Xdoor_5,                            TRUE,   FALSE,
6071     EL_EMC_GATE_5,                      -1, -1
6072   },
6073   {
6074     Xdoor_6,                            TRUE,   FALSE,
6075     EL_EMC_GATE_6,                      -1, -1
6076   },
6077   {
6078     Xdoor_7,                            TRUE,   FALSE,
6079     EL_EMC_GATE_7,                      -1, -1
6080   },
6081   {
6082     Xdoor_8,                            TRUE,   FALSE,
6083     EL_EMC_GATE_8,                      -1, -1
6084   },
6085   {
6086     Xkey_1,                             TRUE,   FALSE,
6087     EL_EM_KEY_1,                        -1, -1
6088   },
6089   {
6090     Xkey_2,                             TRUE,   FALSE,
6091     EL_EM_KEY_2,                        -1, -1
6092   },
6093   {
6094     Xkey_3,                             TRUE,   FALSE,
6095     EL_EM_KEY_3,                        -1, -1
6096   },
6097   {
6098     Xkey_4,                             TRUE,   FALSE,
6099     EL_EM_KEY_4,                        -1, -1
6100   },
6101   {
6102     Xkey_5,                             TRUE,   FALSE,
6103     EL_EMC_KEY_5,                       -1, -1
6104   },
6105   {
6106     Xkey_6,                             TRUE,   FALSE,
6107     EL_EMC_KEY_6,                       -1, -1
6108   },
6109   {
6110     Xkey_7,                             TRUE,   FALSE,
6111     EL_EMC_KEY_7,                       -1, -1
6112   },
6113   {
6114     Xkey_8,                             TRUE,   FALSE,
6115     EL_EMC_KEY_8,                       -1, -1
6116   },
6117   {
6118     Xwind_n,                            TRUE,   FALSE,
6119     EL_BALLOON_SWITCH_UP,               -1, -1
6120   },
6121   {
6122     Xwind_e,                            TRUE,   FALSE,
6123     EL_BALLOON_SWITCH_RIGHT,            -1, -1
6124   },
6125   {
6126     Xwind_s,                            TRUE,   FALSE,
6127     EL_BALLOON_SWITCH_DOWN,             -1, -1
6128   },
6129   {
6130     Xwind_w,                            TRUE,   FALSE,
6131     EL_BALLOON_SWITCH_LEFT,             -1, -1
6132   },
6133   {
6134     Xwind_nesw,                         TRUE,   FALSE,
6135     EL_BALLOON_SWITCH_ANY,              -1, -1
6136   },
6137   {
6138     Xwind_stop,                         TRUE,   FALSE,
6139     EL_BALLOON_SWITCH_NONE,             -1, -1
6140   },
6141   {
6142     Xexit,                              TRUE,   FALSE,
6143     EL_EM_EXIT_CLOSED,                  -1, -1
6144   },
6145   {
6146     Xexit_1,                            TRUE,   FALSE,
6147     EL_EM_EXIT_OPEN,                    -1, -1
6148   },
6149   {
6150     Xexit_2,                            FALSE,  FALSE,
6151     EL_EM_EXIT_OPEN,                    -1, -1
6152   },
6153   {
6154     Xexit_3,                            FALSE,  FALSE,
6155     EL_EM_EXIT_OPEN,                    -1, -1
6156   },
6157   {
6158     Xdynamite,                          TRUE,   FALSE,
6159     EL_EM_DYNAMITE,                     -1, -1
6160   },
6161   {
6162     Ydynamite_eat,                      FALSE,  FALSE,
6163     EL_EM_DYNAMITE,                     ACTION_COLLECTING, -1
6164   },
6165   {
6166     Xdynamite_1,                        TRUE,   FALSE,
6167     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6168   },
6169   {
6170     Xdynamite_2,                        FALSE,  FALSE,
6171     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6172   },
6173   {
6174     Xdynamite_3,                        FALSE,  FALSE,
6175     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6176   },
6177   {
6178     Xdynamite_4,                        FALSE,  FALSE,
6179     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6180   },
6181   {
6182     Xbumper,                            TRUE,   FALSE,
6183     EL_EMC_SPRING_BUMPER,               -1, -1
6184   },
6185   {
6186     XbumperB,                           FALSE,  FALSE,
6187     EL_EMC_SPRING_BUMPER,               ACTION_ACTIVE, -1
6188   },
6189   {
6190     Xwheel,                             TRUE,   FALSE,
6191     EL_ROBOT_WHEEL,                     -1, -1
6192   },
6193   {
6194     XwheelB,                            FALSE,  FALSE,
6195     EL_ROBOT_WHEEL,                     ACTION_ACTIVE, -1
6196   },
6197   {
6198     Xswitch,                            TRUE,   FALSE,
6199     EL_EMC_MAGIC_BALL_SWITCH,           -1, -1
6200   },
6201   {
6202     XswitchB,                           FALSE,  FALSE,
6203     EL_EMC_MAGIC_BALL_SWITCH,           ACTION_ACTIVE, -1
6204   },
6205   {
6206     Xsand,                              TRUE,   FALSE,
6207     EL_QUICKSAND_EMPTY,                 -1, -1
6208   },
6209   {
6210     Xsand_stone,                        TRUE,   FALSE,
6211     EL_QUICKSAND_FULL,                  -1, -1
6212   },
6213   {
6214     Xsand_stonein_1,                    FALSE,  TRUE,
6215     EL_ROCK,                            ACTION_FILLING, -1
6216   },
6217   {
6218     Xsand_stonein_2,                    FALSE,  TRUE,
6219     EL_ROCK,                            ACTION_FILLING, -1
6220   },
6221   {
6222     Xsand_stonein_3,                    FALSE,  TRUE,
6223     EL_ROCK,                            ACTION_FILLING, -1
6224   },
6225   {
6226     Xsand_stonein_4,                    FALSE,  TRUE,
6227     EL_ROCK,                            ACTION_FILLING, -1
6228   },
6229   {
6230     Xsand_stonesand_1,                  FALSE,  FALSE,
6231     EL_QUICKSAND_EMPTYING,              -1, -1
6232   },
6233   {
6234     Xsand_stonesand_2,                  FALSE,  FALSE,
6235     EL_QUICKSAND_EMPTYING,              -1, -1
6236   },
6237   {
6238     Xsand_stonesand_3,                  FALSE,  FALSE,
6239     EL_QUICKSAND_EMPTYING,              -1, -1
6240   },
6241   {
6242     Xsand_stonesand_4,                  FALSE,  FALSE,
6243     EL_QUICKSAND_EMPTYING,              -1, -1
6244   },
6245   {
6246     Xsand_stonesand_quickout_1,         FALSE,  FALSE,
6247     EL_QUICKSAND_EMPTYING,              -1, -1
6248   },
6249   {
6250     Xsand_stonesand_quickout_2,         FALSE,  FALSE,
6251     EL_QUICKSAND_EMPTYING,              -1, -1
6252   },
6253   {
6254     Xsand_stoneout_1,                   FALSE,  FALSE,
6255     EL_ROCK,                            ACTION_EMPTYING, -1
6256   },
6257   {
6258     Xsand_stoneout_2,                   FALSE,  FALSE,
6259     EL_ROCK,                            ACTION_EMPTYING, -1
6260   },
6261   {
6262     Xsand_sandstone_1,                  FALSE,  FALSE,
6263     EL_QUICKSAND_FILLING,               -1, -1
6264   },
6265   {
6266     Xsand_sandstone_2,                  FALSE,  FALSE,
6267     EL_QUICKSAND_FILLING,               -1, -1
6268   },
6269   {
6270     Xsand_sandstone_3,                  FALSE,  FALSE,
6271     EL_QUICKSAND_FILLING,               -1, -1
6272   },
6273   {
6274     Xsand_sandstone_4,                  FALSE,  FALSE,
6275     EL_QUICKSAND_FILLING,               -1, -1
6276   },
6277   {
6278     Xplant,                             TRUE,   FALSE,
6279     EL_EMC_PLANT,                       -1, -1
6280   },
6281   {
6282     Yplant,                             FALSE,  FALSE,
6283     EL_EMC_PLANT,                       -1, -1
6284   },
6285   {
6286     Xlenses,                            TRUE,   FALSE,
6287     EL_EMC_LENSES,                      -1, -1
6288   },
6289   {
6290     Xmagnify,                           TRUE,   FALSE,
6291     EL_EMC_MAGNIFIER,                   -1, -1
6292   },
6293   {
6294     Xdripper,                           TRUE,   FALSE,
6295     EL_EMC_DRIPPER,                     -1, -1
6296   },
6297   {
6298     XdripperB,                          FALSE,  FALSE,
6299     EL_EMC_DRIPPER,                     ACTION_ACTIVE, -1
6300   },
6301   {
6302     Xfake_blank,                        TRUE,   FALSE,
6303     EL_INVISIBLE_WALL,                  -1, -1
6304   },
6305   {
6306     Xfake_blankB,                       FALSE,  FALSE,
6307     EL_INVISIBLE_WALL,                  ACTION_ACTIVE, -1
6308   },
6309   {
6310     Xfake_grass,                        TRUE,   FALSE,
6311     EL_EMC_FAKE_GRASS,                  -1, -1
6312   },
6313   {
6314     Xfake_grassB,                       FALSE,  FALSE,
6315     EL_EMC_FAKE_GRASS,                  ACTION_ACTIVE, -1
6316   },
6317   {
6318     Xfake_door_1,                       TRUE,   FALSE,
6319     EL_EM_GATE_1_GRAY,                  -1, -1
6320   },
6321   {
6322     Xfake_door_2,                       TRUE,   FALSE,
6323     EL_EM_GATE_2_GRAY,                  -1, -1
6324   },
6325   {
6326     Xfake_door_3,                       TRUE,   FALSE,
6327     EL_EM_GATE_3_GRAY,                  -1, -1
6328   },
6329   {
6330     Xfake_door_4,                       TRUE,   FALSE,
6331     EL_EM_GATE_4_GRAY,                  -1, -1
6332   },
6333   {
6334     Xfake_door_5,                       TRUE,   FALSE,
6335     EL_EMC_GATE_5_GRAY,                 -1, -1
6336   },
6337   {
6338     Xfake_door_6,                       TRUE,   FALSE,
6339     EL_EMC_GATE_6_GRAY,                 -1, -1
6340   },
6341   {
6342     Xfake_door_7,                       TRUE,   FALSE,
6343     EL_EMC_GATE_7_GRAY,                 -1, -1
6344   },
6345   {
6346     Xfake_door_8,                       TRUE,   FALSE,
6347     EL_EMC_GATE_8_GRAY,                 -1, -1
6348   },
6349   {
6350     Xfake_acid_1,                       TRUE,   FALSE,
6351     EL_EMC_FAKE_ACID,                   -1, -1
6352   },
6353   {
6354     Xfake_acid_2,                       FALSE,  FALSE,
6355     EL_EMC_FAKE_ACID,                   -1, -1
6356   },
6357   {
6358     Xfake_acid_3,                       FALSE,  FALSE,
6359     EL_EMC_FAKE_ACID,                   -1, -1
6360   },
6361   {
6362     Xfake_acid_4,                       FALSE,  FALSE,
6363     EL_EMC_FAKE_ACID,                   -1, -1
6364   },
6365   {
6366     Xfake_acid_5,                       FALSE,  FALSE,
6367     EL_EMC_FAKE_ACID,                   -1, -1
6368   },
6369   {
6370     Xfake_acid_6,                       FALSE,  FALSE,
6371     EL_EMC_FAKE_ACID,                   -1, -1
6372   },
6373   {
6374     Xfake_acid_7,                       FALSE,  FALSE,
6375     EL_EMC_FAKE_ACID,                   -1, -1
6376   },
6377   {
6378     Xfake_acid_8,                       FALSE,  FALSE,
6379     EL_EMC_FAKE_ACID,                   -1, -1
6380   },
6381   {
6382     Xsteel_1,                           TRUE,   FALSE,
6383     EL_STEELWALL,                       -1, -1
6384   },
6385   {
6386     Xsteel_2,                           TRUE,   FALSE,
6387     EL_EMC_STEELWALL_2,                 -1, -1
6388   },
6389   {
6390     Xsteel_3,                           TRUE,   FALSE,
6391     EL_EMC_STEELWALL_3,                 -1, -1
6392   },
6393   {
6394     Xsteel_4,                           TRUE,   FALSE,
6395     EL_EMC_STEELWALL_4,                 -1, -1
6396   },
6397   {
6398     Xwall_1,                            TRUE,   FALSE,
6399     EL_WALL,                            -1, -1
6400   },
6401   {
6402     Xwall_2,                            TRUE,   FALSE,
6403     EL_EMC_WALL_14,                     -1, -1
6404   },
6405   {
6406     Xwall_3,                            TRUE,   FALSE,
6407     EL_EMC_WALL_15,                     -1, -1
6408   },
6409   {
6410     Xwall_4,                            TRUE,   FALSE,
6411     EL_EMC_WALL_16,                     -1, -1
6412   },
6413   {
6414     Xround_wall_1,                      TRUE,   FALSE,
6415     EL_WALL_SLIPPERY,                   -1, -1
6416   },
6417   {
6418     Xround_wall_2,                      TRUE,   FALSE,
6419     EL_EMC_WALL_SLIPPERY_2,             -1, -1
6420   },
6421   {
6422     Xround_wall_3,                      TRUE,   FALSE,
6423     EL_EMC_WALL_SLIPPERY_3,             -1, -1
6424   },
6425   {
6426     Xround_wall_4,                      TRUE,   FALSE,
6427     EL_EMC_WALL_SLIPPERY_4,             -1, -1
6428   },
6429   {
6430     Xdecor_1,                           TRUE,   FALSE,
6431     EL_EMC_WALL_8,                      -1, -1
6432   },
6433   {
6434     Xdecor_2,                           TRUE,   FALSE,
6435     EL_EMC_WALL_6,                      -1, -1
6436   },
6437   {
6438     Xdecor_3,                           TRUE,   FALSE,
6439     EL_EMC_WALL_4,                      -1, -1
6440   },
6441   {
6442     Xdecor_4,                           TRUE,   FALSE,
6443     EL_EMC_WALL_7,                      -1, -1
6444   },
6445   {
6446     Xdecor_5,                           TRUE,   FALSE,
6447     EL_EMC_WALL_5,                      -1, -1
6448   },
6449   {
6450     Xdecor_6,                           TRUE,   FALSE,
6451     EL_EMC_WALL_9,                      -1, -1
6452   },
6453   {
6454     Xdecor_7,                           TRUE,   FALSE,
6455     EL_EMC_WALL_10,                     -1, -1
6456   },
6457   {
6458     Xdecor_8,                           TRUE,   FALSE,
6459     EL_EMC_WALL_1,                      -1, -1
6460   },
6461   {
6462     Xdecor_9,                           TRUE,   FALSE,
6463     EL_EMC_WALL_2,                      -1, -1
6464   },
6465   {
6466     Xdecor_10,                          TRUE,   FALSE,
6467     EL_EMC_WALL_3,                      -1, -1
6468   },
6469   {
6470     Xdecor_11,                          TRUE,   FALSE,
6471     EL_EMC_WALL_11,                     -1, -1
6472   },
6473   {
6474     Xdecor_12,                          TRUE,   FALSE,
6475     EL_EMC_WALL_12,                     -1, -1
6476   },
6477   {
6478     Xalpha_0,                           TRUE,   FALSE,
6479     EL_CHAR('0'),                       -1, -1
6480   },
6481   {
6482     Xalpha_1,                           TRUE,   FALSE,
6483     EL_CHAR('1'),                       -1, -1
6484   },
6485   {
6486     Xalpha_2,                           TRUE,   FALSE,
6487     EL_CHAR('2'),                       -1, -1
6488   },
6489   {
6490     Xalpha_3,                           TRUE,   FALSE,
6491     EL_CHAR('3'),                       -1, -1
6492   },
6493   {
6494     Xalpha_4,                           TRUE,   FALSE,
6495     EL_CHAR('4'),                       -1, -1
6496   },
6497   {
6498     Xalpha_5,                           TRUE,   FALSE,
6499     EL_CHAR('5'),                       -1, -1
6500   },
6501   {
6502     Xalpha_6,                           TRUE,   FALSE,
6503     EL_CHAR('6'),                       -1, -1
6504   },
6505   {
6506     Xalpha_7,                           TRUE,   FALSE,
6507     EL_CHAR('7'),                       -1, -1
6508   },
6509   {
6510     Xalpha_8,                           TRUE,   FALSE,
6511     EL_CHAR('8'),                       -1, -1
6512   },
6513   {
6514     Xalpha_9,                           TRUE,   FALSE,
6515     EL_CHAR('9'),                       -1, -1
6516   },
6517   {
6518     Xalpha_excla,                       TRUE,   FALSE,
6519     EL_CHAR('!'),                       -1, -1
6520   },
6521   {
6522     Xalpha_quote,                       TRUE,   FALSE,
6523     EL_CHAR('"'),                       -1, -1
6524   },
6525   {
6526     Xalpha_comma,                       TRUE,   FALSE,
6527     EL_CHAR(','),                       -1, -1
6528   },
6529   {
6530     Xalpha_minus,                       TRUE,   FALSE,
6531     EL_CHAR('-'),                       -1, -1
6532   },
6533   {
6534     Xalpha_perio,                       TRUE,   FALSE,
6535     EL_CHAR('.'),                       -1, -1
6536   },
6537   {
6538     Xalpha_colon,                       TRUE,   FALSE,
6539     EL_CHAR(':'),                       -1, -1
6540   },
6541   {
6542     Xalpha_quest,                       TRUE,   FALSE,
6543     EL_CHAR('?'),                       -1, -1
6544   },
6545   {
6546     Xalpha_a,                           TRUE,   FALSE,
6547     EL_CHAR('A'),                       -1, -1
6548   },
6549   {
6550     Xalpha_b,                           TRUE,   FALSE,
6551     EL_CHAR('B'),                       -1, -1
6552   },
6553   {
6554     Xalpha_c,                           TRUE,   FALSE,
6555     EL_CHAR('C'),                       -1, -1
6556   },
6557   {
6558     Xalpha_d,                           TRUE,   FALSE,
6559     EL_CHAR('D'),                       -1, -1
6560   },
6561   {
6562     Xalpha_e,                           TRUE,   FALSE,
6563     EL_CHAR('E'),                       -1, -1
6564   },
6565   {
6566     Xalpha_f,                           TRUE,   FALSE,
6567     EL_CHAR('F'),                       -1, -1
6568   },
6569   {
6570     Xalpha_g,                           TRUE,   FALSE,
6571     EL_CHAR('G'),                       -1, -1
6572   },
6573   {
6574     Xalpha_h,                           TRUE,   FALSE,
6575     EL_CHAR('H'),                       -1, -1
6576   },
6577   {
6578     Xalpha_i,                           TRUE,   FALSE,
6579     EL_CHAR('I'),                       -1, -1
6580   },
6581   {
6582     Xalpha_j,                           TRUE,   FALSE,
6583     EL_CHAR('J'),                       -1, -1
6584   },
6585   {
6586     Xalpha_k,                           TRUE,   FALSE,
6587     EL_CHAR('K'),                       -1, -1
6588   },
6589   {
6590     Xalpha_l,                           TRUE,   FALSE,
6591     EL_CHAR('L'),                       -1, -1
6592   },
6593   {
6594     Xalpha_m,                           TRUE,   FALSE,
6595     EL_CHAR('M'),                       -1, -1
6596   },
6597   {
6598     Xalpha_n,                           TRUE,   FALSE,
6599     EL_CHAR('N'),                       -1, -1
6600   },
6601   {
6602     Xalpha_o,                           TRUE,   FALSE,
6603     EL_CHAR('O'),                       -1, -1
6604   },
6605   {
6606     Xalpha_p,                           TRUE,   FALSE,
6607     EL_CHAR('P'),                       -1, -1
6608   },
6609   {
6610     Xalpha_q,                           TRUE,   FALSE,
6611     EL_CHAR('Q'),                       -1, -1
6612   },
6613   {
6614     Xalpha_r,                           TRUE,   FALSE,
6615     EL_CHAR('R'),                       -1, -1
6616   },
6617   {
6618     Xalpha_s,                           TRUE,   FALSE,
6619     EL_CHAR('S'),                       -1, -1
6620   },
6621   {
6622     Xalpha_t,                           TRUE,   FALSE,
6623     EL_CHAR('T'),                       -1, -1
6624   },
6625   {
6626     Xalpha_u,                           TRUE,   FALSE,
6627     EL_CHAR('U'),                       -1, -1
6628   },
6629   {
6630     Xalpha_v,                           TRUE,   FALSE,
6631     EL_CHAR('V'),                       -1, -1
6632   },
6633   {
6634     Xalpha_w,                           TRUE,   FALSE,
6635     EL_CHAR('W'),                       -1, -1
6636   },
6637   {
6638     Xalpha_x,                           TRUE,   FALSE,
6639     EL_CHAR('X'),                       -1, -1
6640   },
6641   {
6642     Xalpha_y,                           TRUE,   FALSE,
6643     EL_CHAR('Y'),                       -1, -1
6644   },
6645   {
6646     Xalpha_z,                           TRUE,   FALSE,
6647     EL_CHAR('Z'),                       -1, -1
6648   },
6649   {
6650     Xalpha_arrow_e,                     TRUE,   FALSE,
6651     EL_CHAR('>'),                       -1, -1
6652   },
6653   {
6654     Xalpha_arrow_w,                     TRUE,   FALSE,
6655     EL_CHAR('<'),                       -1, -1
6656   },
6657   {
6658     Xalpha_copyr,                       TRUE,   FALSE,
6659     EL_CHAR(CHAR_BYTE_COPYRIGHT),       -1, -1
6660   },
6661
6662   {
6663     Xboom_bug,                          FALSE,  FALSE,
6664     EL_BUG,                             ACTION_EXPLODING, -1
6665   },
6666   {
6667     Xboom_bomb,                         FALSE,  FALSE,
6668     EL_BOMB,                            ACTION_EXPLODING, -1
6669   },
6670   {
6671     Xboom_android,                      FALSE,  FALSE,
6672     EL_EMC_ANDROID,                     ACTION_OTHER, -1
6673   },
6674   {
6675     Xboom_1,                            FALSE,  FALSE,
6676     EL_DEFAULT,                         ACTION_EXPLODING, -1
6677   },
6678   {
6679     Xboom_2,                            FALSE,  FALSE,
6680     EL_DEFAULT,                         ACTION_EXPLODING, -1
6681   },
6682   {
6683     Znormal,                            FALSE,  FALSE,
6684     EL_EMPTY,                           -1, -1
6685   },
6686   {
6687     Zdynamite,                          FALSE,  FALSE,
6688     EL_EMPTY,                           -1, -1
6689   },
6690   {
6691     Zplayer,                            FALSE,  FALSE,
6692     EL_EMPTY,                           -1, -1
6693   },
6694   {
6695     ZBORDER,                            FALSE,  FALSE,
6696     EL_EMPTY,                           -1, -1
6697   },
6698
6699   {
6700     -1,                                 FALSE,  FALSE,
6701     -1,                                 -1, -1
6702   }
6703 };
6704
6705 static struct Mapping_EM_to_RND_player
6706 {
6707   int action_em;
6708   int player_nr;
6709
6710   int element_rnd;
6711   int action;
6712   int direction;
6713 }
6714 em_player_mapping_list[] =
6715 {
6716   {
6717     SPR_walk + 0,                       0,
6718     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_UP,
6719   },
6720   {
6721     SPR_walk + 1,                       0,
6722     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_RIGHT,
6723   },
6724   {
6725     SPR_walk + 2,                       0,
6726     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_DOWN,
6727   },
6728   {
6729     SPR_walk + 3,                       0,
6730     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_LEFT,
6731   },
6732   {
6733     SPR_push + 0,                       0,
6734     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_UP,
6735   },
6736   {
6737     SPR_push + 1,                       0,
6738     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_RIGHT,
6739   },
6740   {
6741     SPR_push + 2,                       0,
6742     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_DOWN,
6743   },
6744   {
6745     SPR_push + 3,                       0,
6746     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_LEFT,
6747   },
6748   {
6749     SPR_spray + 0,                      0,
6750     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_UP,
6751   },
6752   {
6753     SPR_spray + 1,                      0,
6754     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_RIGHT,
6755   },
6756   {
6757     SPR_spray + 2,                      0,
6758     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_DOWN,
6759   },
6760   {
6761     SPR_spray + 3,                      0,
6762     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_LEFT,
6763   },
6764   {
6765     SPR_walk + 0,                       1,
6766     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_UP,
6767   },
6768   {
6769     SPR_walk + 1,                       1,
6770     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_RIGHT,
6771   },
6772   {
6773     SPR_walk + 2,                       1,
6774     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_DOWN,
6775   },
6776   {
6777     SPR_walk + 3,                       1,
6778     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_LEFT,
6779   },
6780   {
6781     SPR_push + 0,                       1,
6782     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_UP,
6783   },
6784   {
6785     SPR_push + 1,                       1,
6786     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_RIGHT,
6787   },
6788   {
6789     SPR_push + 2,                       1,
6790     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_DOWN,
6791   },
6792   {
6793     SPR_push + 3,                       1,
6794     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_LEFT,
6795   },
6796   {
6797     SPR_spray + 0,                      1,
6798     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_UP,
6799   },
6800   {
6801     SPR_spray + 1,                      1,
6802     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_RIGHT,
6803   },
6804   {
6805     SPR_spray + 2,                      1,
6806     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_DOWN,
6807   },
6808   {
6809     SPR_spray + 3,                      1,
6810     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_LEFT,
6811   },
6812   {
6813     SPR_still,                          0,
6814     EL_PLAYER_1,                        ACTION_DEFAULT, -1,
6815   },
6816   {
6817     SPR_still,                          1,
6818     EL_PLAYER_2,                        ACTION_DEFAULT, -1,
6819   },
6820   {
6821     SPR_walk + 0,                       2,
6822     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_UP,
6823   },
6824   {
6825     SPR_walk + 1,                       2,
6826     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_RIGHT,
6827   },
6828   {
6829     SPR_walk + 2,                       2,
6830     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_DOWN,
6831   },
6832   {
6833     SPR_walk + 3,                       2,
6834     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_LEFT,
6835   },
6836   {
6837     SPR_push + 0,                       2,
6838     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_UP,
6839   },
6840   {
6841     SPR_push + 1,                       2,
6842     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_RIGHT,
6843   },
6844   {
6845     SPR_push + 2,                       2,
6846     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_DOWN,
6847   },
6848   {
6849     SPR_push + 3,                       2,
6850     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_LEFT,
6851   },
6852   {
6853     SPR_spray + 0,                      2,
6854     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_UP,
6855   },
6856   {
6857     SPR_spray + 1,                      2,
6858     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_RIGHT,
6859   },
6860   {
6861     SPR_spray + 2,                      2,
6862     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_DOWN,
6863   },
6864   {
6865     SPR_spray + 3,                      2,
6866     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_LEFT,
6867   },
6868   {
6869     SPR_walk + 0,                       3,
6870     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_UP,
6871   },
6872   {
6873     SPR_walk + 1,                       3,
6874     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_RIGHT,
6875   },
6876   {
6877     SPR_walk + 2,                       3,
6878     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_DOWN,
6879   },
6880   {
6881     SPR_walk + 3,                       3,
6882     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_LEFT,
6883   },
6884   {
6885     SPR_push + 0,                       3,
6886     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_UP,
6887   },
6888   {
6889     SPR_push + 1,                       3,
6890     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_RIGHT,
6891   },
6892   {
6893     SPR_push + 2,                       3,
6894     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_DOWN,
6895   },
6896   {
6897     SPR_push + 3,                       3,
6898     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_LEFT,
6899   },
6900   {
6901     SPR_spray + 0,                      3,
6902     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_UP,
6903   },
6904   {
6905     SPR_spray + 1,                      3,
6906     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_RIGHT,
6907   },
6908   {
6909     SPR_spray + 2,                      3,
6910     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_DOWN,
6911   },
6912   {
6913     SPR_spray + 3,                      3,
6914     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_LEFT,
6915   },
6916   {
6917     SPR_still,                          2,
6918     EL_PLAYER_3,                        ACTION_DEFAULT, -1,
6919   },
6920   {
6921     SPR_still,                          3,
6922     EL_PLAYER_4,                        ACTION_DEFAULT, -1,
6923   },
6924
6925   {
6926     -1,                                 -1,
6927     -1,                                 -1, -1
6928   }
6929 };
6930
6931 int map_element_RND_to_EM(int element_rnd)
6932 {
6933   static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6934   static boolean mapping_initialized = FALSE;
6935
6936   if (!mapping_initialized)
6937   {
6938     int i;
6939
6940     /* return "Xalpha_quest" for all undefined elements in mapping array */
6941     for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6942       mapping_RND_to_EM[i] = Xalpha_quest;
6943
6944     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6945       if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6946         mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6947           em_object_mapping_list[i].element_em;
6948
6949     mapping_initialized = TRUE;
6950   }
6951
6952   if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6953     return mapping_RND_to_EM[element_rnd];
6954
6955   Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6956
6957   return EL_UNKNOWN;
6958 }
6959
6960 int map_element_EM_to_RND(int element_em)
6961 {
6962   static unsigned short mapping_EM_to_RND[TILE_MAX];
6963   static boolean mapping_initialized = FALSE;
6964
6965   if (!mapping_initialized)
6966   {
6967     int i;
6968
6969     /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6970     for (i = 0; i < TILE_MAX; i++)
6971       mapping_EM_to_RND[i] = EL_UNKNOWN;
6972
6973     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6974       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6975         em_object_mapping_list[i].element_rnd;
6976
6977     mapping_initialized = TRUE;
6978   }
6979
6980   if (element_em >= 0 && element_em < TILE_MAX)
6981     return mapping_EM_to_RND[element_em];
6982
6983   Error(ERR_WARN, "invalid EM level element %d", element_em);
6984
6985   return EL_UNKNOWN;
6986 }
6987
6988 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6989 {
6990   struct LevelInfo_EM *level_em = level->native_em_level;
6991   struct LEVEL *lev = level_em->lev;
6992   int i, j;
6993
6994   for (i = 0; i < TILE_MAX; i++)
6995     lev->android_array[i] = Xblank;
6996
6997   for (i = 0; i < level->num_android_clone_elements; i++)
6998   {
6999     int element_rnd = level->android_clone_element[i];
7000     int element_em = map_element_RND_to_EM(element_rnd);
7001
7002     for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7003       if (em_object_mapping_list[j].element_rnd == element_rnd)
7004         lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7005   }
7006 }
7007
7008 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7009 {
7010   struct LevelInfo_EM *level_em = level->native_em_level;
7011   struct LEVEL *lev = level_em->lev;
7012   int i, j;
7013
7014   level->num_android_clone_elements = 0;
7015
7016   for (i = 0; i < TILE_MAX; i++)
7017   {
7018     int element_em = lev->android_array[i];
7019     int element_rnd;
7020     boolean element_found = FALSE;
7021
7022     if (element_em == Xblank)
7023       continue;
7024
7025     element_rnd = map_element_EM_to_RND(element_em);
7026
7027     for (j = 0; j < level->num_android_clone_elements; j++)
7028       if (level->android_clone_element[j] == element_rnd)
7029         element_found = TRUE;
7030
7031     if (!element_found)
7032     {
7033       level->android_clone_element[level->num_android_clone_elements++] =
7034         element_rnd;
7035
7036       if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7037         break;
7038     }
7039   }
7040
7041   if (level->num_android_clone_elements == 0)
7042   {
7043     level->num_android_clone_elements = 1;
7044     level->android_clone_element[0] = EL_EMPTY;
7045   }
7046 }
7047
7048 int map_direction_RND_to_EM(int direction)
7049 {
7050   return (direction == MV_UP    ? 0 :
7051           direction == MV_RIGHT ? 1 :
7052           direction == MV_DOWN  ? 2 :
7053           direction == MV_LEFT  ? 3 :
7054           -1);
7055 }
7056
7057 int map_direction_EM_to_RND(int direction)
7058 {
7059   return (direction == 0 ? MV_UP    :
7060           direction == 1 ? MV_RIGHT :
7061           direction == 2 ? MV_DOWN  :
7062           direction == 3 ? MV_LEFT  :
7063           MV_NONE);
7064 }
7065
7066 int map_element_RND_to_SP(int element_rnd)
7067 {
7068   int element_sp = 0x20;        /* map unknown elements to yellow "hardware" */
7069
7070   if (element_rnd >= EL_SP_START &&
7071       element_rnd <= EL_SP_END)
7072     element_sp = element_rnd - EL_SP_START;
7073   else if (element_rnd == EL_EMPTY_SPACE)
7074     element_sp = 0x00;
7075   else if (element_rnd == EL_INVISIBLE_WALL)
7076     element_sp = 0x28;
7077
7078   return element_sp;
7079 }
7080
7081 int map_element_SP_to_RND(int element_sp)
7082 {
7083   int element_rnd = EL_UNKNOWN;
7084
7085   if (element_sp >= 0x00 &&
7086       element_sp <= 0x27)
7087     element_rnd = EL_SP_START + element_sp;
7088   else if (element_sp == 0x28)
7089     element_rnd = EL_INVISIBLE_WALL;
7090
7091   return element_rnd;
7092 }
7093
7094 int map_action_SP_to_RND(int action_sp)
7095 {
7096   switch (action_sp)
7097   {
7098     case actActive:             return ACTION_ACTIVE;
7099     case actImpact:             return ACTION_IMPACT;
7100     case actExploding:          return ACTION_EXPLODING;
7101     case actDigging:            return ACTION_DIGGING;
7102     case actSnapping:           return ACTION_SNAPPING;
7103     case actCollecting:         return ACTION_COLLECTING;
7104     case actPassing:            return ACTION_PASSING;
7105     case actPushing:            return ACTION_PUSHING;
7106     case actDropping:           return ACTION_DROPPING;
7107
7108     default:                    return ACTION_DEFAULT;
7109   }
7110 }
7111
7112 int get_next_element(int element)
7113 {
7114   switch (element)
7115   {
7116     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
7117     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
7118     case EL_QUICKSAND_FAST_FILLING:     return EL_QUICKSAND_FAST_FULL;
7119     case EL_QUICKSAND_FAST_EMPTYING:    return EL_QUICKSAND_FAST_EMPTY;
7120     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
7121     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
7122     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
7123     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
7124     case EL_DC_MAGIC_WALL_FILLING:      return EL_DC_MAGIC_WALL_FULL;
7125     case EL_DC_MAGIC_WALL_EMPTYING:     return EL_DC_MAGIC_WALL_ACTIVE;
7126     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
7127
7128     default:                            return element;
7129   }
7130 }
7131
7132 int el_act_dir2img(int element, int action, int direction)
7133 {
7134   element = GFX_ELEMENT(element);
7135   direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7136
7137   /* direction_graphic[][] == graphic[] for undefined direction graphics */
7138   return element_info[element].direction_graphic[action][direction];
7139 }
7140
7141 static int el_act_dir2crm(int element, int action, int direction)
7142 {
7143   element = GFX_ELEMENT(element);
7144   direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7145
7146   /* direction_graphic[][] == graphic[] for undefined direction graphics */
7147   return element_info[element].direction_crumbled[action][direction];
7148 }
7149
7150 int el_act2img(int element, int action)
7151 {
7152   element = GFX_ELEMENT(element);
7153
7154   return element_info[element].graphic[action];
7155 }
7156
7157 int el_act2crm(int element, int action)
7158 {
7159   element = GFX_ELEMENT(element);
7160
7161   return element_info[element].crumbled[action];
7162 }
7163
7164 int el_dir2img(int element, int direction)
7165 {
7166   element = GFX_ELEMENT(element);
7167
7168   return el_act_dir2img(element, ACTION_DEFAULT, direction);
7169 }
7170
7171 int el2baseimg(int element)
7172 {
7173   return element_info[element].graphic[ACTION_DEFAULT];
7174 }
7175
7176 int el2img(int element)
7177 {
7178   element = GFX_ELEMENT(element);
7179
7180   return element_info[element].graphic[ACTION_DEFAULT];
7181 }
7182
7183 int el2edimg(int element)
7184 {
7185   element = GFX_ELEMENT(element);
7186
7187   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7188 }
7189
7190 int el2preimg(int element)
7191 {
7192   element = GFX_ELEMENT(element);
7193
7194   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7195 }
7196
7197 int el2panelimg(int element)
7198 {
7199   element = GFX_ELEMENT(element);
7200
7201   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7202 }
7203
7204 int font2baseimg(int font_nr)
7205 {
7206   return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7207 }
7208
7209 int getBeltNrFromBeltElement(int element)
7210 {
7211   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7212           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7213           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7214 }
7215
7216 int getBeltNrFromBeltActiveElement(int element)
7217 {
7218   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7219           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7220           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7221 }
7222
7223 int getBeltNrFromBeltSwitchElement(int element)
7224 {
7225   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
7226           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
7227           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
7228 }
7229
7230 int getBeltDirNrFromBeltElement(int element)
7231 {
7232   static int belt_base_element[4] =
7233   {
7234     EL_CONVEYOR_BELT_1_LEFT,
7235     EL_CONVEYOR_BELT_2_LEFT,
7236     EL_CONVEYOR_BELT_3_LEFT,
7237     EL_CONVEYOR_BELT_4_LEFT
7238   };
7239
7240   int belt_nr = getBeltNrFromBeltElement(element);
7241   int belt_dir_nr = element - belt_base_element[belt_nr];
7242
7243   return (belt_dir_nr % 3);
7244 }
7245
7246 int getBeltDirNrFromBeltSwitchElement(int element)
7247 {
7248   static int belt_base_element[4] =
7249   {
7250     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7251     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7252     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7253     EL_CONVEYOR_BELT_4_SWITCH_LEFT
7254   };
7255
7256   int belt_nr = getBeltNrFromBeltSwitchElement(element);
7257   int belt_dir_nr = element - belt_base_element[belt_nr];
7258
7259   return (belt_dir_nr % 3);
7260 }
7261
7262 int getBeltDirFromBeltElement(int element)
7263 {
7264   static int belt_move_dir[3] =
7265   {
7266     MV_LEFT,
7267     MV_NONE,
7268     MV_RIGHT
7269   };
7270
7271   int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7272
7273   return belt_move_dir[belt_dir_nr];
7274 }
7275
7276 int getBeltDirFromBeltSwitchElement(int element)
7277 {
7278   static int belt_move_dir[3] =
7279   {
7280     MV_LEFT,
7281     MV_NONE,
7282     MV_RIGHT
7283   };
7284
7285   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7286
7287   return belt_move_dir[belt_dir_nr];
7288 }
7289
7290 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7291 {
7292   static int belt_base_element[4] =
7293   {
7294     EL_CONVEYOR_BELT_1_LEFT,
7295     EL_CONVEYOR_BELT_2_LEFT,
7296     EL_CONVEYOR_BELT_3_LEFT,
7297     EL_CONVEYOR_BELT_4_LEFT
7298   };
7299
7300   return belt_base_element[belt_nr] + belt_dir_nr;
7301 }
7302
7303 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7304 {
7305   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7306
7307   return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7308 }
7309
7310 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7311 {
7312   static int belt_base_element[4] =
7313   {
7314     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7315     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7316     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7317     EL_CONVEYOR_BELT_4_SWITCH_LEFT
7318   };
7319
7320   return belt_base_element[belt_nr] + belt_dir_nr;
7321 }
7322
7323 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7324 {
7325   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7326
7327   return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7328 }
7329
7330 boolean getTeamMode_EM()
7331 {
7332   return game.team_mode;
7333 }
7334
7335 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7336 {
7337   int game_frame_delay_value;
7338
7339   game_frame_delay_value =
7340     (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7341      GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7342      GameFrameDelay);
7343
7344   if (tape.playing && tape.warp_forward && !tape.pausing)
7345     game_frame_delay_value = 0;
7346
7347   return game_frame_delay_value;
7348 }
7349
7350 unsigned int InitRND(int seed)
7351 {
7352   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7353     return InitEngineRandom_EM(seed);
7354   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7355     return InitEngineRandom_SP(seed);
7356   else
7357     return InitEngineRandom_RND(seed);
7358 }
7359
7360 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7361 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7362
7363 inline static int get_effective_element_EM(int tile, int frame_em)
7364 {
7365   int element             = object_mapping[tile].element_rnd;
7366   int action              = object_mapping[tile].action;
7367   boolean is_backside     = object_mapping[tile].is_backside;
7368   boolean action_removing = (action == ACTION_DIGGING ||
7369                              action == ACTION_SNAPPING ||
7370                              action == ACTION_COLLECTING);
7371
7372   if (frame_em < 7)
7373   {
7374     switch (tile)
7375     {
7376       case Yacid_splash_eB:
7377       case Yacid_splash_wB:
7378         return (frame_em > 5 ? EL_EMPTY : element);
7379
7380       default:
7381         return element;
7382     }
7383   }
7384   else  /* frame_em == 7 */
7385   {
7386     switch (tile)
7387     {
7388       case Yacid_splash_eB:
7389       case Yacid_splash_wB:
7390         return EL_EMPTY;
7391
7392       case Yemerald_stone:
7393         return EL_EMERALD;
7394
7395       case Ydiamond_stone:
7396         return EL_ROCK;
7397
7398       case Xdrip_stretch:
7399       case Xdrip_stretchB:
7400       case Ydrip_s1:
7401       case Ydrip_s1B:
7402       case Xball_1B:
7403       case Xball_2:
7404       case Xball_2B:
7405       case Yball_eat:
7406       case Ykey_1_eat:
7407       case Ykey_2_eat:
7408       case Ykey_3_eat:
7409       case Ykey_4_eat:
7410       case Ykey_5_eat:
7411       case Ykey_6_eat:
7412       case Ykey_7_eat:
7413       case Ykey_8_eat:
7414       case Ylenses_eat:
7415       case Ymagnify_eat:
7416       case Ygrass_eat:
7417       case Ydirt_eat:
7418       case Xsand_stonein_1:
7419       case Xsand_stonein_2:
7420       case Xsand_stonein_3:
7421       case Xsand_stonein_4:
7422         return element;
7423
7424       default:
7425         return (is_backside || action_removing ? EL_EMPTY : element);
7426     }
7427   }
7428 }
7429
7430 inline static boolean check_linear_animation_EM(int tile)
7431 {
7432   switch (tile)
7433   {
7434     case Xsand_stonesand_1:
7435     case Xsand_stonesand_quickout_1:
7436     case Xsand_sandstone_1:
7437     case Xsand_stonein_1:
7438     case Xsand_stoneout_1:
7439     case Xboom_1:
7440     case Xdynamite_1:
7441     case Ybug_w_n:
7442     case Ybug_n_e:
7443     case Ybug_e_s:
7444     case Ybug_s_w:
7445     case Ybug_e_n:
7446     case Ybug_s_e:
7447     case Ybug_w_s:
7448     case Ybug_n_w:
7449     case Ytank_w_n:
7450     case Ytank_n_e:
7451     case Ytank_e_s:
7452     case Ytank_s_w:
7453     case Ytank_e_n:
7454     case Ytank_s_e:
7455     case Ytank_w_s:
7456     case Ytank_n_w:
7457     case Yacid_splash_eB:
7458     case Yacid_splash_wB:
7459     case Yemerald_stone:
7460       return TRUE;
7461   }
7462
7463   return FALSE;
7464 }
7465
7466 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7467                                             boolean has_crumbled_graphics,
7468                                             int crumbled, int sync_frame)
7469 {
7470   /* if element can be crumbled, but certain action graphics are just empty
7471      space (like instantly snapping sand to empty space in 1 frame), do not
7472      treat these empty space graphics as crumbled graphics in EMC engine */
7473   if (crumbled == IMG_EMPTY_SPACE)
7474     has_crumbled_graphics = FALSE;
7475
7476   if (has_crumbled_graphics)
7477   {
7478     struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7479     int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7480                                            g_crumbled->anim_delay,
7481                                            g_crumbled->anim_mode,
7482                                            g_crumbled->anim_start_frame,
7483                                            sync_frame);
7484
7485     getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7486                      &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7487
7488     g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7489     g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
7490
7491     g_em->has_crumbled_graphics = TRUE;
7492   }
7493   else
7494   {
7495     g_em->crumbled_bitmap = NULL;
7496     g_em->crumbled_src_x = 0;
7497     g_em->crumbled_src_y = 0;
7498     g_em->crumbled_border_size = 0;
7499     g_em->crumbled_tile_size = 0;
7500
7501     g_em->has_crumbled_graphics = FALSE;
7502   }
7503 }
7504
7505 void ResetGfxAnimation_EM(int x, int y, int tile)
7506 {
7507   GfxFrame[x][y] = 0;
7508 }
7509
7510 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7511                         int tile, int frame_em, int x, int y)
7512 {
7513   int action = object_mapping[tile].action;
7514   int direction = object_mapping[tile].direction;
7515   int effective_element = get_effective_element_EM(tile, frame_em);
7516   int graphic = (direction == MV_NONE ?
7517                  el_act2img(effective_element, action) :
7518                  el_act_dir2img(effective_element, action, direction));
7519   struct GraphicInfo *g = &graphic_info[graphic];
7520   int sync_frame;
7521   boolean action_removing = (action == ACTION_DIGGING ||
7522                              action == ACTION_SNAPPING ||
7523                              action == ACTION_COLLECTING);
7524   boolean action_moving   = (action == ACTION_FALLING ||
7525                              action == ACTION_MOVING ||
7526                              action == ACTION_PUSHING ||
7527                              action == ACTION_EATING ||
7528                              action == ACTION_FILLING ||
7529                              action == ACTION_EMPTYING);
7530   boolean action_falling  = (action == ACTION_FALLING ||
7531                              action == ACTION_FILLING ||
7532                              action == ACTION_EMPTYING);
7533
7534   /* special case: graphic uses "2nd movement tile" and has defined
7535      7 frames for movement animation (or less) => use default graphic
7536      for last (8th) frame which ends the movement animation */
7537   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7538   {
7539     action = ACTION_DEFAULT;    /* (keep action_* unchanged for now) */
7540     graphic = (direction == MV_NONE ?
7541                el_act2img(effective_element, action) :
7542                el_act_dir2img(effective_element, action, direction));
7543
7544     g = &graphic_info[graphic];
7545   }
7546
7547   if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7548   {
7549     GfxFrame[x][y] = 0;
7550   }
7551   else if (action_moving)
7552   {
7553     boolean is_backside = object_mapping[tile].is_backside;
7554
7555     if (is_backside)
7556     {
7557       int direction = object_mapping[tile].direction;
7558       int move_dir = (action_falling ? MV_DOWN : direction);
7559
7560       GfxFrame[x][y]++;
7561
7562 #if 1
7563       /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7564       if (g->double_movement && frame_em == 0)
7565         GfxFrame[x][y] = 0;
7566 #endif
7567
7568       if (move_dir == MV_LEFT)
7569         GfxFrame[x - 1][y] = GfxFrame[x][y];
7570       else if (move_dir == MV_RIGHT)
7571         GfxFrame[x + 1][y] = GfxFrame[x][y];
7572       else if (move_dir == MV_UP)
7573         GfxFrame[x][y - 1] = GfxFrame[x][y];
7574       else if (move_dir == MV_DOWN)
7575         GfxFrame[x][y + 1] = GfxFrame[x][y];
7576     }
7577   }
7578   else
7579   {
7580     GfxFrame[x][y]++;
7581
7582     /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7583     if (tile == Xsand_stonesand_quickout_1 ||
7584         tile == Xsand_stonesand_quickout_2)
7585       GfxFrame[x][y]++;
7586   }
7587
7588   if (graphic_info[graphic].anim_global_sync)
7589     sync_frame = FrameCounter;
7590   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7591     sync_frame = GfxFrame[x][y];
7592   else
7593     sync_frame = 0;     /* playfield border (pseudo steel) */
7594
7595   SetRandomAnimationValue(x, y);
7596
7597   int frame = getAnimationFrame(g->anim_frames,
7598                                 g->anim_delay,
7599                                 g->anim_mode,
7600                                 g->anim_start_frame,
7601                                 sync_frame);
7602
7603   g_em->unique_identifier =
7604     (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7605 }
7606
7607 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7608                                   int tile, int frame_em, int x, int y)
7609 {
7610   int action = object_mapping[tile].action;
7611   int direction = object_mapping[tile].direction;
7612   boolean is_backside = object_mapping[tile].is_backside;
7613   int effective_element = get_effective_element_EM(tile, frame_em);
7614   int effective_action = action;
7615   int graphic = (direction == MV_NONE ?
7616                  el_act2img(effective_element, effective_action) :
7617                  el_act_dir2img(effective_element, effective_action,
7618                                 direction));
7619   int crumbled = (direction == MV_NONE ?
7620                   el_act2crm(effective_element, effective_action) :
7621                   el_act_dir2crm(effective_element, effective_action,
7622                                  direction));
7623   int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7624   int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7625   boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7626   struct GraphicInfo *g = &graphic_info[graphic];
7627   int sync_frame;
7628
7629   /* special case: graphic uses "2nd movement tile" and has defined
7630      7 frames for movement animation (or less) => use default graphic
7631      for last (8th) frame which ends the movement animation */
7632   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7633   {
7634     effective_action = ACTION_DEFAULT;
7635     graphic = (direction == MV_NONE ?
7636                el_act2img(effective_element, effective_action) :
7637                el_act_dir2img(effective_element, effective_action,
7638                               direction));
7639     crumbled = (direction == MV_NONE ?
7640                 el_act2crm(effective_element, effective_action) :
7641                 el_act_dir2crm(effective_element, effective_action,
7642                                direction));
7643
7644     g = &graphic_info[graphic];
7645   }
7646
7647   if (graphic_info[graphic].anim_global_sync)
7648     sync_frame = FrameCounter;
7649   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7650     sync_frame = GfxFrame[x][y];
7651   else
7652     sync_frame = 0;     /* playfield border (pseudo steel) */
7653
7654   SetRandomAnimationValue(x, y);
7655
7656   int frame = getAnimationFrame(g->anim_frames,
7657                                 g->anim_delay,
7658                                 g->anim_mode,
7659                                 g->anim_start_frame,
7660                                 sync_frame);
7661
7662   getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7663                       g->double_movement && is_backside);
7664
7665   /* (updating the "crumbled" graphic definitions is probably not really needed,
7666      as animations for crumbled graphics can't be longer than one EMC cycle) */
7667   set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7668                            sync_frame);
7669 }
7670
7671 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7672                                   int player_nr, int anim, int frame_em)
7673 {
7674   int element   = player_mapping[player_nr][anim].element_rnd;
7675   int action    = player_mapping[player_nr][anim].action;
7676   int direction = player_mapping[player_nr][anim].direction;
7677   int graphic = (direction == MV_NONE ?
7678                  el_act2img(element, action) :
7679                  el_act_dir2img(element, action, direction));
7680   struct GraphicInfo *g = &graphic_info[graphic];
7681   int sync_frame;
7682
7683   InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7684
7685   stored_player[player_nr].StepFrame = frame_em;
7686
7687   sync_frame = stored_player[player_nr].Frame;
7688
7689   int frame = getAnimationFrame(g->anim_frames,
7690                                 g->anim_delay,
7691                                 g->anim_mode,
7692                                 g->anim_start_frame,
7693                                 sync_frame);
7694
7695   getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7696                       &g_em->src_x, &g_em->src_y, FALSE);
7697 }
7698
7699 void InitGraphicInfo_EM(void)
7700 {
7701   int i, j, p;
7702
7703 #if DEBUG_EM_GFX
7704   int num_em_gfx_errors = 0;
7705
7706   if (graphic_info_em_object[0][0].bitmap == NULL)
7707   {
7708     /* EM graphics not yet initialized in em_open_all() */
7709
7710     return;
7711   }
7712
7713   printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7714 #endif
7715
7716   /* always start with reliable default values */
7717   for (i = 0; i < TILE_MAX; i++)
7718   {
7719     object_mapping[i].element_rnd = EL_UNKNOWN;
7720     object_mapping[i].is_backside = FALSE;
7721     object_mapping[i].action = ACTION_DEFAULT;
7722     object_mapping[i].direction = MV_NONE;
7723   }
7724
7725   /* always start with reliable default values */
7726   for (p = 0; p < MAX_PLAYERS; p++)
7727   {
7728     for (i = 0; i < SPR_MAX; i++)
7729     {
7730       player_mapping[p][i].element_rnd = EL_UNKNOWN;
7731       player_mapping[p][i].action = ACTION_DEFAULT;
7732       player_mapping[p][i].direction = MV_NONE;
7733     }
7734   }
7735
7736   for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7737   {
7738     int e = em_object_mapping_list[i].element_em;
7739
7740     object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7741     object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7742
7743     if (em_object_mapping_list[i].action != -1)
7744       object_mapping[e].action = em_object_mapping_list[i].action;
7745
7746     if (em_object_mapping_list[i].direction != -1)
7747       object_mapping[e].direction =
7748         MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7749   }
7750
7751   for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7752   {
7753     int a = em_player_mapping_list[i].action_em;
7754     int p = em_player_mapping_list[i].player_nr;
7755
7756     player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7757
7758     if (em_player_mapping_list[i].action != -1)
7759       player_mapping[p][a].action = em_player_mapping_list[i].action;
7760
7761     if (em_player_mapping_list[i].direction != -1)
7762       player_mapping[p][a].direction =
7763         MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7764   }
7765
7766   for (i = 0; i < TILE_MAX; i++)
7767   {
7768     int element = object_mapping[i].element_rnd;
7769     int action = object_mapping[i].action;
7770     int direction = object_mapping[i].direction;
7771     boolean is_backside = object_mapping[i].is_backside;
7772     boolean action_exploding = ((action == ACTION_EXPLODING ||
7773                                  action == ACTION_SMASHED_BY_ROCK ||
7774                                  action == ACTION_SMASHED_BY_SPRING) &&
7775                                 element != EL_DIAMOND);
7776     boolean action_active = (action == ACTION_ACTIVE);
7777     boolean action_other = (action == ACTION_OTHER);
7778
7779     for (j = 0; j < 8; j++)
7780     {
7781       int effective_element = get_effective_element_EM(i, j);
7782       int effective_action = (j < 7 ? action :
7783                               i == Xdrip_stretch ? action :
7784                               i == Xdrip_stretchB ? action :
7785                               i == Ydrip_s1 ? action :
7786                               i == Ydrip_s1B ? action :
7787                               i == Xball_1B ? action :
7788                               i == Xball_2 ? action :
7789                               i == Xball_2B ? action :
7790                               i == Yball_eat ? action :
7791                               i == Ykey_1_eat ? action :
7792                               i == Ykey_2_eat ? action :
7793                               i == Ykey_3_eat ? action :
7794                               i == Ykey_4_eat ? action :
7795                               i == Ykey_5_eat ? action :
7796                               i == Ykey_6_eat ? action :
7797                               i == Ykey_7_eat ? action :
7798                               i == Ykey_8_eat ? action :
7799                               i == Ylenses_eat ? action :
7800                               i == Ymagnify_eat ? action :
7801                               i == Ygrass_eat ? action :
7802                               i == Ydirt_eat ? action :
7803                               i == Xsand_stonein_1 ? action :
7804                               i == Xsand_stonein_2 ? action :
7805                               i == Xsand_stonein_3 ? action :
7806                               i == Xsand_stonein_4 ? action :
7807                               i == Xsand_stoneout_1 ? action :
7808                               i == Xsand_stoneout_2 ? action :
7809                               i == Xboom_android ? ACTION_EXPLODING :
7810                               action_exploding ? ACTION_EXPLODING :
7811                               action_active ? action :
7812                               action_other ? action :
7813                               ACTION_DEFAULT);
7814       int graphic = (el_act_dir2img(effective_element, effective_action,
7815                                     direction));
7816       int crumbled = (el_act_dir2crm(effective_element, effective_action,
7817                                      direction));
7818       int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7819       int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7820       boolean has_action_graphics = (graphic != base_graphic);
7821       boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7822       struct GraphicInfo *g = &graphic_info[graphic];
7823       struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7824       Bitmap *src_bitmap;
7825       int src_x, src_y;
7826       /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7827       boolean special_animation = (action != ACTION_DEFAULT &&
7828                                    g->anim_frames == 3 &&
7829                                    g->anim_delay == 2 &&
7830                                    g->anim_mode & ANIM_LINEAR);
7831       int sync_frame = (i == Xdrip_stretch ? 7 :
7832                         i == Xdrip_stretchB ? 7 :
7833                         i == Ydrip_s2 ? j + 8 :
7834                         i == Ydrip_s2B ? j + 8 :
7835                         i == Xacid_1 ? 0 :
7836                         i == Xacid_2 ? 10 :
7837                         i == Xacid_3 ? 20 :
7838                         i == Xacid_4 ? 30 :
7839                         i == Xacid_5 ? 40 :
7840                         i == Xacid_6 ? 50 :
7841                         i == Xacid_7 ? 60 :
7842                         i == Xacid_8 ? 70 :
7843                         i == Xfake_acid_1 ? 0 :
7844                         i == Xfake_acid_2 ? 10 :
7845                         i == Xfake_acid_3 ? 20 :
7846                         i == Xfake_acid_4 ? 30 :
7847                         i == Xfake_acid_5 ? 40 :
7848                         i == Xfake_acid_6 ? 50 :
7849                         i == Xfake_acid_7 ? 60 :
7850                         i == Xfake_acid_8 ? 70 :
7851                         i == Xball_2 ? 7 :
7852                         i == Xball_2B ? j + 8 :
7853                         i == Yball_eat ? j + 1 :
7854                         i == Ykey_1_eat ? j + 1 :
7855                         i == Ykey_2_eat ? j + 1 :
7856                         i == Ykey_3_eat ? j + 1 :
7857                         i == Ykey_4_eat ? j + 1 :
7858                         i == Ykey_5_eat ? j + 1 :
7859                         i == Ykey_6_eat ? j + 1 :
7860                         i == Ykey_7_eat ? j + 1 :
7861                         i == Ykey_8_eat ? j + 1 :
7862                         i == Ylenses_eat ? j + 1 :
7863                         i == Ymagnify_eat ? j + 1 :
7864                         i == Ygrass_eat ? j + 1 :
7865                         i == Ydirt_eat ? j + 1 :
7866                         i == Xamoeba_1 ? 0 :
7867                         i == Xamoeba_2 ? 1 :
7868                         i == Xamoeba_3 ? 2 :
7869                         i == Xamoeba_4 ? 3 :
7870                         i == Xamoeba_5 ? 0 :
7871                         i == Xamoeba_6 ? 1 :
7872                         i == Xamoeba_7 ? 2 :
7873                         i == Xamoeba_8 ? 3 :
7874                         i == Xexit_2 ? j + 8 :
7875                         i == Xexit_3 ? j + 16 :
7876                         i == Xdynamite_1 ? 0 :
7877                         i == Xdynamite_2 ? 8 :
7878                         i == Xdynamite_3 ? 16 :
7879                         i == Xdynamite_4 ? 24 :
7880                         i == Xsand_stonein_1 ? j + 1 :
7881                         i == Xsand_stonein_2 ? j + 9 :
7882                         i == Xsand_stonein_3 ? j + 17 :
7883                         i == Xsand_stonein_4 ? j + 25 :
7884                         i == Xsand_stoneout_1 && j == 0 ? 0 :
7885                         i == Xsand_stoneout_1 && j == 1 ? 0 :
7886                         i == Xsand_stoneout_1 && j == 2 ? 1 :
7887                         i == Xsand_stoneout_1 && j == 3 ? 2 :
7888                         i == Xsand_stoneout_1 && j == 4 ? 2 :
7889                         i == Xsand_stoneout_1 && j == 5 ? 3 :
7890                         i == Xsand_stoneout_1 && j == 6 ? 4 :
7891                         i == Xsand_stoneout_1 && j == 7 ? 4 :
7892                         i == Xsand_stoneout_2 && j == 0 ? 5 :
7893                         i == Xsand_stoneout_2 && j == 1 ? 6 :
7894                         i == Xsand_stoneout_2 && j == 2 ? 7 :
7895                         i == Xsand_stoneout_2 && j == 3 ? 8 :
7896                         i == Xsand_stoneout_2 && j == 4 ? 9 :
7897                         i == Xsand_stoneout_2 && j == 5 ? 11 :
7898                         i == Xsand_stoneout_2 && j == 6 ? 13 :
7899                         i == Xsand_stoneout_2 && j == 7 ? 15 :
7900                         i == Xboom_bug && j == 1 ? 2 :
7901                         i == Xboom_bug && j == 2 ? 2 :
7902                         i == Xboom_bug && j == 3 ? 4 :
7903                         i == Xboom_bug && j == 4 ? 4 :
7904                         i == Xboom_bug && j == 5 ? 2 :
7905                         i == Xboom_bug && j == 6 ? 2 :
7906                         i == Xboom_bug && j == 7 ? 0 :
7907                         i == Xboom_bomb && j == 1 ? 2 :
7908                         i == Xboom_bomb && j == 2 ? 2 :
7909                         i == Xboom_bomb && j == 3 ? 4 :
7910                         i == Xboom_bomb && j == 4 ? 4 :
7911                         i == Xboom_bomb && j == 5 ? 2 :
7912                         i == Xboom_bomb && j == 6 ? 2 :
7913                         i == Xboom_bomb && j == 7 ? 0 :
7914                         i == Xboom_android && j == 7 ? 6 :
7915                         i == Xboom_1 && j == 1 ? 2 :
7916                         i == Xboom_1 && j == 2 ? 2 :
7917                         i == Xboom_1 && j == 3 ? 4 :
7918                         i == Xboom_1 && j == 4 ? 4 :
7919                         i == Xboom_1 && j == 5 ? 6 :
7920                         i == Xboom_1 && j == 6 ? 6 :
7921                         i == Xboom_1 && j == 7 ? 8 :
7922                         i == Xboom_2 && j == 0 ? 8 :
7923                         i == Xboom_2 && j == 1 ? 8 :
7924                         i == Xboom_2 && j == 2 ? 10 :
7925                         i == Xboom_2 && j == 3 ? 10 :
7926                         i == Xboom_2 && j == 4 ? 10 :
7927                         i == Xboom_2 && j == 5 ? 12 :
7928                         i == Xboom_2 && j == 6 ? 12 :
7929                         i == Xboom_2 && j == 7 ? 12 :
7930                         special_animation && j == 4 ? 3 :
7931                         effective_action != action ? 0 :
7932                         j);
7933
7934 #if DEBUG_EM_GFX
7935       Bitmap *debug_bitmap = g_em->bitmap;
7936       int debug_src_x = g_em->src_x;
7937       int debug_src_y = g_em->src_y;
7938 #endif
7939
7940       int frame = getAnimationFrame(g->anim_frames,
7941                                     g->anim_delay,
7942                                     g->anim_mode,
7943                                     g->anim_start_frame,
7944                                     sync_frame);
7945
7946       getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7947                           g->double_movement && is_backside);
7948
7949       g_em->bitmap = src_bitmap;
7950       g_em->src_x = src_x;
7951       g_em->src_y = src_y;
7952       g_em->src_offset_x = 0;
7953       g_em->src_offset_y = 0;
7954       g_em->dst_offset_x = 0;
7955       g_em->dst_offset_y = 0;
7956       g_em->width  = TILEX;
7957       g_em->height = TILEY;
7958
7959       g_em->preserve_background = FALSE;
7960
7961       set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7962                                sync_frame);
7963
7964       if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7965                                    effective_action == ACTION_MOVING  ||
7966                                    effective_action == ACTION_PUSHING ||
7967                                    effective_action == ACTION_EATING)) ||
7968           (!has_action_graphics && (effective_action == ACTION_FILLING ||
7969                                     effective_action == ACTION_EMPTYING)))
7970       {
7971         int move_dir =
7972           (effective_action == ACTION_FALLING ||
7973            effective_action == ACTION_FILLING ||
7974            effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7975         int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7976         int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? 1 : 0);
7977         int num_steps = (i == Ydrip_s1  ? 16 :
7978                          i == Ydrip_s1B ? 16 :
7979                          i == Ydrip_s2  ? 16 :
7980                          i == Ydrip_s2B ? 16 :
7981                          i == Xsand_stonein_1 ? 32 :
7982                          i == Xsand_stonein_2 ? 32 :
7983                          i == Xsand_stonein_3 ? 32 :
7984                          i == Xsand_stonein_4 ? 32 :
7985                          i == Xsand_stoneout_1 ? 16 :
7986                          i == Xsand_stoneout_2 ? 16 : 8);
7987         int cx = ABS(dx) * (TILEX / num_steps);
7988         int cy = ABS(dy) * (TILEY / num_steps);
7989         int step_frame = (i == Ydrip_s2         ? j + 8 :
7990                           i == Ydrip_s2B        ? j + 8 :
7991                           i == Xsand_stonein_2  ? j + 8 :
7992                           i == Xsand_stonein_3  ? j + 16 :
7993                           i == Xsand_stonein_4  ? j + 24 :
7994                           i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7995         int step = (is_backside ? step_frame : num_steps - step_frame);
7996
7997         if (is_backside)        /* tile where movement starts */
7998         {
7999           if (dx < 0 || dy < 0)
8000           {
8001             g_em->src_offset_x = cx * step;
8002             g_em->src_offset_y = cy * step;
8003           }
8004           else
8005           {
8006             g_em->dst_offset_x = cx * step;
8007             g_em->dst_offset_y = cy * step;
8008           }
8009         }
8010         else                    /* tile where movement ends */
8011         {
8012           if (dx < 0 || dy < 0)
8013           {
8014             g_em->dst_offset_x = cx * step;
8015             g_em->dst_offset_y = cy * step;
8016           }
8017           else
8018           {
8019             g_em->src_offset_x = cx * step;
8020             g_em->src_offset_y = cy * step;
8021           }
8022         }
8023
8024         g_em->width  = TILEX - cx * step;
8025         g_em->height = TILEY - cy * step;
8026       }
8027
8028       /* create unique graphic identifier to decide if tile must be redrawn */
8029       /* bit 31 - 16 (16 bit): EM style graphic
8030          bit 15 - 12 ( 4 bit): EM style frame
8031          bit 11 -  6 ( 6 bit): graphic width
8032          bit  5 -  0 ( 6 bit): graphic height */
8033       g_em->unique_identifier =
8034         (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8035
8036 #if DEBUG_EM_GFX
8037
8038       /* skip check for EMC elements not contained in original EMC artwork */
8039       if (element == EL_EMC_FAKE_ACID)
8040         continue;
8041
8042       if (g_em->bitmap != debug_bitmap ||
8043           g_em->src_x != debug_src_x ||
8044           g_em->src_y != debug_src_y ||
8045           g_em->src_offset_x != 0 ||
8046           g_em->src_offset_y != 0 ||
8047           g_em->dst_offset_x != 0 ||
8048           g_em->dst_offset_y != 0 ||
8049           g_em->width != TILEX ||
8050           g_em->height != TILEY)
8051       {
8052         static int last_i = -1;
8053
8054         if (i != last_i)
8055         {
8056           printf("\n");
8057           last_i = i;
8058         }
8059
8060         printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8061                i, element, element_info[element].token_name,
8062                element_action_info[effective_action].suffix, direction);
8063
8064         if (element != effective_element)
8065           printf(" [%d ('%s')]",
8066                  effective_element,
8067                  element_info[effective_element].token_name);
8068
8069         printf("\n");
8070
8071         if (g_em->bitmap != debug_bitmap)
8072           printf("    %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8073                  j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8074
8075         if (g_em->src_x != debug_src_x ||
8076             g_em->src_y != debug_src_y)
8077           printf("    frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8078                  j, (is_backside ? 'B' : 'F'),
8079                  g_em->src_x, g_em->src_y,
8080                  g_em->src_x / 32, g_em->src_y / 32,
8081                  debug_src_x, debug_src_y,
8082                  debug_src_x / 32, debug_src_y / 32);
8083
8084         if (g_em->src_offset_x != 0 ||
8085             g_em->src_offset_y != 0 ||
8086             g_em->dst_offset_x != 0 ||
8087             g_em->dst_offset_y != 0)
8088           printf("    %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8089                  j, is_backside,
8090                  g_em->src_offset_x, g_em->src_offset_y,
8091                  g_em->dst_offset_x, g_em->dst_offset_y);
8092
8093         if (g_em->width != TILEX ||
8094             g_em->height != TILEY)
8095           printf("    %d (%d): size %d,%d should be %d,%d\n",
8096                  j, is_backside,
8097                  g_em->width, g_em->height, TILEX, TILEY);
8098
8099         num_em_gfx_errors++;
8100       }
8101 #endif
8102
8103     }
8104   }
8105
8106   for (i = 0; i < TILE_MAX; i++)
8107   {
8108     for (j = 0; j < 8; j++)
8109     {
8110       int element = object_mapping[i].element_rnd;
8111       int action = object_mapping[i].action;
8112       int direction = object_mapping[i].direction;
8113       boolean is_backside = object_mapping[i].is_backside;
8114       int graphic_action  = el_act_dir2img(element, action, direction);
8115       int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8116
8117       if ((action == ACTION_SMASHED_BY_ROCK ||
8118            action == ACTION_SMASHED_BY_SPRING ||
8119            action == ACTION_EATING) &&
8120           graphic_action == graphic_default)
8121       {
8122         int e = (action == ACTION_SMASHED_BY_ROCK   ? Ystone_s  :
8123                  action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8124                  direction == MV_LEFT  ? (is_backside? Yspring_wB: Yspring_w) :
8125                  direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8126                  Xspring);
8127
8128         /* no separate animation for "smashed by rock" -- use rock instead */
8129         struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8130         struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8131
8132         g_em->bitmap            = g_xx->bitmap;
8133         g_em->src_x             = g_xx->src_x;
8134         g_em->src_y             = g_xx->src_y;
8135         g_em->src_offset_x      = g_xx->src_offset_x;
8136         g_em->src_offset_y      = g_xx->src_offset_y;
8137         g_em->dst_offset_x      = g_xx->dst_offset_x;
8138         g_em->dst_offset_y      = g_xx->dst_offset_y;
8139         g_em->width             = g_xx->width;
8140         g_em->height            = g_xx->height;
8141         g_em->unique_identifier = g_xx->unique_identifier;
8142
8143         if (!is_backside)
8144           g_em->preserve_background = TRUE;
8145       }
8146     }
8147   }
8148
8149   for (p = 0; p < MAX_PLAYERS; p++)
8150   {
8151     for (i = 0; i < SPR_MAX; i++)
8152     {
8153       int element = player_mapping[p][i].element_rnd;
8154       int action = player_mapping[p][i].action;
8155       int direction = player_mapping[p][i].direction;
8156
8157       for (j = 0; j < 8; j++)
8158       {
8159         int effective_element = element;
8160         int effective_action = action;
8161         int graphic = (direction == MV_NONE ?
8162                        el_act2img(effective_element, effective_action) :
8163                        el_act_dir2img(effective_element, effective_action,
8164                                       direction));
8165         struct GraphicInfo *g = &graphic_info[graphic];
8166         struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8167         Bitmap *src_bitmap;
8168         int src_x, src_y;
8169         int sync_frame = j;
8170
8171 #if DEBUG_EM_GFX
8172         Bitmap *debug_bitmap = g_em->bitmap;
8173         int debug_src_x = g_em->src_x;
8174         int debug_src_y = g_em->src_y;
8175 #endif
8176
8177         int frame = getAnimationFrame(g->anim_frames,
8178                                       g->anim_delay,
8179                                       g->anim_mode,
8180                                       g->anim_start_frame,
8181                                       sync_frame);
8182
8183         getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8184
8185         g_em->bitmap = src_bitmap;
8186         g_em->src_x = src_x;
8187         g_em->src_y = src_y;
8188         g_em->src_offset_x = 0;
8189         g_em->src_offset_y = 0;
8190         g_em->dst_offset_x = 0;
8191         g_em->dst_offset_y = 0;
8192         g_em->width  = TILEX;
8193         g_em->height = TILEY;
8194
8195 #if DEBUG_EM_GFX
8196
8197         /* skip check for EMC elements not contained in original EMC artwork */
8198         if (element == EL_PLAYER_3 ||
8199             element == EL_PLAYER_4)
8200           continue;
8201
8202         if (g_em->bitmap != debug_bitmap ||
8203             g_em->src_x != debug_src_x ||
8204             g_em->src_y != debug_src_y)
8205         {
8206           static int last_i = -1;
8207
8208           if (i != last_i)
8209           {
8210             printf("\n");
8211             last_i = i;
8212           }
8213
8214           printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8215                  p, i, element, element_info[element].token_name,
8216                  element_action_info[effective_action].suffix, direction);
8217
8218           if (element != effective_element)
8219             printf(" [%d ('%s')]",
8220                    effective_element,
8221                    element_info[effective_element].token_name);
8222
8223           printf("\n");
8224
8225           if (g_em->bitmap != debug_bitmap)
8226             printf("    %d: different bitmap! (0x%08x != 0x%08x)\n",
8227                    j, (int)(g_em->bitmap), (int)(debug_bitmap));
8228
8229           if (g_em->src_x != debug_src_x ||
8230               g_em->src_y != debug_src_y)
8231             printf("    frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8232                    j,
8233                    g_em->src_x, g_em->src_y,
8234                    g_em->src_x / 32, g_em->src_y / 32,
8235                    debug_src_x, debug_src_y,
8236                    debug_src_x / 32, debug_src_y / 32);
8237
8238           num_em_gfx_errors++;
8239         }
8240 #endif
8241
8242       }
8243     }
8244   }
8245
8246 #if DEBUG_EM_GFX
8247   printf("\n");
8248   printf("::: [%d errors found]\n", num_em_gfx_errors);
8249
8250   exit(0);
8251 #endif
8252 }
8253
8254 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8255                                 boolean any_player_moving,
8256                                 boolean any_player_snapping,
8257                                 boolean any_player_dropping)
8258 {
8259   if (frame == 0 && !any_player_dropping)
8260   {
8261     if (!local_player->was_waiting)
8262     {
8263       if (!CheckSaveEngineSnapshotToList())
8264         return;
8265
8266       local_player->was_waiting = TRUE;
8267     }
8268   }
8269   else if (any_player_moving || any_player_snapping || any_player_dropping)
8270   {
8271     local_player->was_waiting = FALSE;
8272   }
8273 }
8274
8275 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8276                                 boolean murphy_is_dropping)
8277 {
8278   if (murphy_is_waiting)
8279   {
8280     if (!local_player->was_waiting)
8281     {
8282       if (!CheckSaveEngineSnapshotToList())
8283         return;
8284
8285       local_player->was_waiting = TRUE;
8286     }
8287   }
8288   else
8289   {
8290     local_player->was_waiting = FALSE;
8291   }
8292 }
8293
8294 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8295                             boolean any_player_moving,
8296                             boolean any_player_snapping,
8297                             boolean any_player_dropping)
8298 {
8299   if (tape.single_step && tape.recording && !tape.pausing)
8300     if (frame == 0 && !any_player_dropping)
8301       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8302
8303   CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8304                              any_player_snapping, any_player_dropping);
8305 }
8306
8307 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8308                             boolean murphy_is_dropping)
8309 {
8310   if (tape.single_step && tape.recording && !tape.pausing)
8311     if (murphy_is_waiting)
8312       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8313
8314   CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8315 }
8316
8317 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8318                          int graphic, int sync_frame, int x, int y)
8319 {
8320   int frame = getGraphicAnimationFrame(graphic, sync_frame);
8321
8322   getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8323 }
8324
8325 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8326 {
8327   return (IS_NEXT_FRAME(sync_frame, graphic));
8328 }
8329
8330 int getGraphicInfo_Delay(int graphic)
8331 {
8332   return graphic_info[graphic].anim_delay;
8333 }
8334
8335 void PlayMenuSoundExt(int sound)
8336 {
8337   if (sound == SND_UNDEFINED)
8338     return;
8339
8340   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8341       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8342     return;
8343
8344   if (IS_LOOP_SOUND(sound))
8345     PlaySoundLoop(sound);
8346   else
8347     PlaySound(sound);
8348 }
8349
8350 void PlayMenuSound()
8351 {
8352   PlayMenuSoundExt(menu.sound[game_status]);
8353 }
8354
8355 void PlayMenuSoundStereo(int sound, int stereo_position)
8356 {
8357   if (sound == SND_UNDEFINED)
8358     return;
8359
8360   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8361       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8362     return;
8363
8364   if (IS_LOOP_SOUND(sound))
8365     PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8366   else
8367     PlaySoundStereo(sound, stereo_position);
8368 }
8369
8370 void PlayMenuSoundIfLoopExt(int sound)
8371 {
8372   if (sound == SND_UNDEFINED)
8373     return;
8374
8375   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8376       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8377     return;
8378
8379   if (IS_LOOP_SOUND(sound))
8380     PlaySoundLoop(sound);
8381 }
8382
8383 void PlayMenuSoundIfLoop()
8384 {
8385   PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8386 }
8387
8388 void PlayMenuMusicExt(int music)
8389 {
8390   if (music == MUS_UNDEFINED)
8391     return;
8392
8393   if (!setup.sound_music)
8394     return;
8395
8396   PlayMusic(music);
8397 }
8398
8399 void PlayMenuMusic()
8400 {
8401   PlayMenuMusicExt(menu.music[game_status]);
8402 }
8403
8404 void PlaySoundActivating()
8405 {
8406 #if 0
8407   PlaySound(SND_MENU_ITEM_ACTIVATING);
8408 #endif
8409 }
8410
8411 void PlaySoundSelecting()
8412 {
8413 #if 0
8414   PlaySound(SND_MENU_ITEM_SELECTING);
8415 #endif
8416 }
8417
8418 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8419 {
8420   boolean change_fullscreen = (setup.fullscreen !=
8421                                video.fullscreen_enabled);
8422   boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8423                                            setup.window_scaling_percent !=
8424                                            video.window_scaling_percent);
8425
8426   if (change_window_scaling_percent && video.fullscreen_enabled)
8427     return;
8428
8429   if (!change_window_scaling_percent && !video.fullscreen_available)
8430     return;
8431
8432 #if defined(TARGET_SDL2)
8433   if (change_window_scaling_percent)
8434   {
8435     SDLSetWindowScaling(setup.window_scaling_percent);
8436
8437     return;
8438   }
8439   else if (change_fullscreen)
8440   {
8441     SDLSetWindowFullscreen(setup.fullscreen);
8442
8443     /* set setup value according to successfully changed fullscreen mode */
8444     setup.fullscreen = video.fullscreen_enabled;
8445
8446     return;
8447   }
8448 #endif
8449
8450   if (change_fullscreen ||
8451       change_window_scaling_percent)
8452   {
8453     Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8454
8455     /* save backbuffer content which gets lost when toggling fullscreen mode */
8456     BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8457
8458     if (change_window_scaling_percent)
8459     {
8460       /* keep window mode, but change window scaling */
8461       video.fullscreen_enabled = TRUE;          /* force new window scaling */
8462     }
8463
8464     /* toggle fullscreen */
8465     ChangeVideoModeIfNeeded(setup.fullscreen);
8466
8467     /* set setup value according to successfully changed fullscreen mode */
8468     setup.fullscreen = video.fullscreen_enabled;
8469
8470     /* restore backbuffer content from temporary backbuffer backup bitmap */
8471     BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8472
8473     FreeBitmap(tmp_backbuffer);
8474
8475     /* update visible window/screen */
8476     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8477   }
8478 }
8479
8480 void JoinRectangles(int *x, int *y, int *width, int *height,
8481                     int x2, int y2, int width2, int height2)
8482 {
8483   // do not join with "off-screen" rectangle
8484   if (x2 == -1 || y2 == -1)
8485     return;
8486
8487   *x = MIN(*x, x2);
8488   *y = MIN(*y, y2);
8489   *width = MAX(*width, width2);
8490   *height = MAX(*height, height2);
8491 }
8492
8493 void SetAnimStatus(int anim_status_new)
8494 {
8495   if (anim_status_new == GAME_MODE_MAIN)
8496     anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
8497
8498   global.anim_status_next = anim_status_new;
8499
8500   // directly set screen modes that are entered without fading
8501   if ((global.anim_status      == GAME_MODE_PSEUDO_MAINONLY &&
8502        global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
8503       (global.anim_status      == GAME_MODE_PSEUDO_TYPENAME &&
8504        global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
8505     global.anim_status = global.anim_status_next;
8506 }
8507
8508 void SetGameStatus(int game_status_new)
8509 {
8510   game_status = game_status_new;
8511
8512   SetAnimStatus(game_status_new);
8513 }
8514
8515 void SetFontStatus(int game_status_new)
8516 {
8517   static int last_game_status = -1;
8518
8519   if (game_status_new != -1)
8520   {
8521     // set game status for font use after storing last game status
8522     last_game_status = game_status;
8523     game_status = game_status_new;
8524   }
8525   else
8526   {
8527     // reset game status after font use from last stored game status
8528     game_status = last_game_status;
8529   }
8530 }
8531
8532 void ResetFontStatus()
8533 {
8534   SetFontStatus(-1);
8535 }
8536
8537 void ChangeViewportPropertiesIfNeeded()
8538 {
8539   int gfx_game_mode = game_status;
8540   int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8541                         game_status);
8542   struct RectWithBorder *vp_window    = &viewport.window[gfx_game_mode];
8543   struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8544   struct RectWithBorder *vp_door_1    = &viewport.door_1[gfx_game_mode];
8545   struct RectWithBorder *vp_door_2    = &viewport.door_2[gfx_game_mode2];
8546   struct RectWithBorder *vp_door_3    = &viewport.door_2[GAME_MODE_EDITOR];
8547   int new_win_xsize     = vp_window->width;
8548   int new_win_ysize     = vp_window->height;
8549   int border_size       = vp_playfield->border_size;
8550   int new_sx            = vp_playfield->x + border_size;
8551   int new_sy            = vp_playfield->y + border_size;
8552   int new_sxsize        = vp_playfield->width  - 2 * border_size;
8553   int new_sysize        = vp_playfield->height - 2 * border_size;
8554   int new_real_sx       = vp_playfield->x;
8555   int new_real_sy       = vp_playfield->y;
8556   int new_full_sxsize   = vp_playfield->width;
8557   int new_full_sysize   = vp_playfield->height;
8558   int new_dx            = vp_door_1->x;
8559   int new_dy            = vp_door_1->y;
8560   int new_dxsize        = vp_door_1->width;
8561   int new_dysize        = vp_door_1->height;
8562   int new_vx            = vp_door_2->x;
8563   int new_vy            = vp_door_2->y;
8564   int new_vxsize        = vp_door_2->width;
8565   int new_vysize        = vp_door_2->height;
8566   int new_ex            = vp_door_3->x;
8567   int new_ey            = vp_door_3->y;
8568   int new_exsize        = vp_door_3->width;
8569   int new_eysize        = vp_door_3->height;
8570   int new_tilesize_var =
8571     (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8572
8573   int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8574                   gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8575   int new_scr_fieldx = new_sxsize / tilesize;
8576   int new_scr_fieldy = new_sysize / tilesize;
8577   int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8578   int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8579   boolean init_gfx_buffers = FALSE;
8580   boolean init_video_buffer = FALSE;
8581   boolean init_gadgets_and_anims = FALSE;
8582   boolean init_em_graphics = FALSE;
8583
8584   if (new_win_xsize != WIN_XSIZE ||
8585       new_win_ysize != WIN_YSIZE)
8586   {
8587     WIN_XSIZE = new_win_xsize;
8588     WIN_YSIZE = new_win_ysize;
8589
8590     init_video_buffer = TRUE;
8591     init_gfx_buffers = TRUE;
8592     init_gadgets_and_anims = TRUE;
8593
8594     // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8595   }
8596
8597   if (new_scr_fieldx != SCR_FIELDX ||
8598       new_scr_fieldy != SCR_FIELDY)
8599   {
8600     /* this always toggles between MAIN and GAME when using small tile size */
8601
8602     SCR_FIELDX = new_scr_fieldx;
8603     SCR_FIELDY = new_scr_fieldy;
8604
8605     // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8606   }
8607
8608   if (new_sx != SX ||
8609       new_sy != SY ||
8610       new_dx != DX ||
8611       new_dy != DY ||
8612       new_vx != VX ||
8613       new_vy != VY ||
8614       new_ex != EX ||
8615       new_ey != EY ||
8616       new_sxsize != SXSIZE ||
8617       new_sysize != SYSIZE ||
8618       new_dxsize != DXSIZE ||
8619       new_dysize != DYSIZE ||
8620       new_vxsize != VXSIZE ||
8621       new_vysize != VYSIZE ||
8622       new_exsize != EXSIZE ||
8623       new_eysize != EYSIZE ||
8624       new_real_sx != REAL_SX ||
8625       new_real_sy != REAL_SY ||
8626       new_full_sxsize != FULL_SXSIZE ||
8627       new_full_sysize != FULL_SYSIZE ||
8628       new_tilesize_var != TILESIZE_VAR
8629       )
8630   {
8631     // ------------------------------------------------------------------------
8632     // determine next fading area for changed viewport definitions
8633     // ------------------------------------------------------------------------
8634
8635     // start with current playfield area (default fading area)
8636     FADE_SX = REAL_SX;
8637     FADE_SY = REAL_SY;
8638     FADE_SXSIZE = FULL_SXSIZE;
8639     FADE_SYSIZE = FULL_SYSIZE;
8640
8641     // add new playfield area if position or size has changed
8642     if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8643         new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8644     {
8645       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8646                      new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8647     }
8648
8649     // add current and new door 1 area if position or size has changed
8650     if (new_dx != DX || new_dy != DY ||
8651         new_dxsize != DXSIZE || new_dysize != DYSIZE)
8652     {
8653       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8654                      DX, DY, DXSIZE, DYSIZE);
8655       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8656                      new_dx, new_dy, new_dxsize, new_dysize);
8657     }
8658
8659     // add current and new door 2 area if position or size has changed
8660     if (new_dx != VX || new_dy != VY ||
8661         new_dxsize != VXSIZE || new_dysize != VYSIZE)
8662     {
8663       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8664                      VX, VY, VXSIZE, VYSIZE);
8665       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8666                      new_vx, new_vy, new_vxsize, new_vysize);
8667     }
8668
8669     // ------------------------------------------------------------------------
8670     // handle changed tile size
8671     // ------------------------------------------------------------------------
8672
8673     if (new_tilesize_var != TILESIZE_VAR)
8674     {
8675       // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8676
8677       // changing tile size invalidates scroll values of engine snapshots
8678       FreeEngineSnapshotSingle();
8679
8680       // changing tile size requires update of graphic mapping for EM engine
8681       init_em_graphics = TRUE;
8682     }
8683
8684     SX = new_sx;
8685     SY = new_sy;
8686     DX = new_dx;
8687     DY = new_dy;
8688     VX = new_vx;
8689     VY = new_vy;
8690     EX = new_ex;
8691     EY = new_ey;
8692     SXSIZE = new_sxsize;
8693     SYSIZE = new_sysize;
8694     DXSIZE = new_dxsize;
8695     DYSIZE = new_dysize;
8696     VXSIZE = new_vxsize;
8697     VYSIZE = new_vysize;
8698     EXSIZE = new_exsize;
8699     EYSIZE = new_eysize;
8700     REAL_SX = new_real_sx;
8701     REAL_SY = new_real_sy;
8702     FULL_SXSIZE = new_full_sxsize;
8703     FULL_SYSIZE = new_full_sysize;
8704     TILESIZE_VAR = new_tilesize_var;
8705
8706     init_gfx_buffers = TRUE;
8707     init_gadgets_and_anims = TRUE;
8708
8709     // printf("::: viewports: init_gfx_buffers\n");
8710     // printf("::: viewports: init_gadgets_and_anims\n");
8711   }
8712
8713   if (init_gfx_buffers)
8714   {
8715     // printf("::: init_gfx_buffers\n");
8716
8717     SCR_FIELDX = new_scr_fieldx_buffers;
8718     SCR_FIELDY = new_scr_fieldy_buffers;
8719
8720     InitGfxBuffers();
8721
8722     SCR_FIELDX = new_scr_fieldx;
8723     SCR_FIELDY = new_scr_fieldy;
8724
8725     SetDrawDeactivationMask(REDRAW_NONE);
8726     SetDrawBackgroundMask(REDRAW_FIELD);
8727   }
8728
8729   if (init_video_buffer)
8730   {
8731     // printf("::: init_video_buffer\n");
8732
8733     InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8734     InitImageTextures();
8735   }
8736
8737   if (init_gadgets_and_anims)
8738   {
8739     // printf("::: init_gadgets_and_anims\n");
8740
8741     InitGadgets();
8742     InitGlobalAnimations();
8743   }
8744
8745   if (init_em_graphics)
8746   {
8747       InitGraphicInfo_EM();
8748   }
8749 }