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