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