added function to get music filename by music ID
[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     Event event;
3781
3782     if (NextValidEvent(&event))
3783     {
3784       switch (event.type)
3785       {
3786         case EVENT_BUTTONPRESS:
3787         case EVENT_KEYPRESS:
3788 #if defined(TARGET_SDL2)
3789         case SDL_CONTROLLERBUTTONDOWN:
3790 #endif
3791         case SDL_JOYBUTTONDOWN:
3792           still_wait = FALSE;
3793           break;
3794
3795         case EVENT_KEYRELEASE:
3796           ClearPlayerAction();
3797           break;
3798
3799         default:
3800           HandleOtherEvents(&event);
3801           break;
3802       }
3803     }
3804     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3805     {
3806       still_wait = FALSE;
3807     }
3808
3809     BackToFront();
3810   }
3811 }
3812
3813 #define MAX_REQUEST_LINES               13
3814 #define MAX_REQUEST_LINE_FONT1_LEN      7
3815 #define MAX_REQUEST_LINE_FONT2_LEN      10
3816
3817 static int RequestHandleEvents(unsigned int req_state)
3818 {
3819   boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3820                           local_player->LevelSolved_GameEnd);
3821   int width  = request.width;
3822   int height = request.height;
3823   int sx, sy;
3824   int result;
3825
3826   setRequestPosition(&sx, &sy, FALSE);
3827
3828   button_status = MB_RELEASED;
3829
3830   request_gadget_id = -1;
3831   result = -1;
3832
3833   while (result < 0)
3834   {
3835     if (level_solved)
3836     {
3837       SetDrawtoField(DRAW_TO_FIELDBUFFER);
3838
3839       HandleGameActions();
3840
3841       SetDrawtoField(DRAW_TO_BACKBUFFER);
3842
3843       if (global.use_envelope_request)
3844       {
3845         /* copy current state of request area to middle of playfield area */
3846         BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
3847       }
3848     }
3849
3850     if (PendingEvent())
3851     {
3852       Event event;
3853
3854       while (NextValidEvent(&event))
3855       {
3856         switch (event.type)
3857         {
3858           case EVENT_BUTTONPRESS:
3859           case EVENT_BUTTONRELEASE:
3860           case EVENT_MOTIONNOTIFY:
3861           {
3862             int mx, my;
3863
3864             if (event.type == EVENT_MOTIONNOTIFY)
3865             {
3866               if (!button_status)
3867                 continue;
3868
3869               motion_status = TRUE;
3870               mx = ((MotionEvent *) &event)->x;
3871               my = ((MotionEvent *) &event)->y;
3872             }
3873             else
3874             {
3875               motion_status = FALSE;
3876               mx = ((ButtonEvent *) &event)->x;
3877               my = ((ButtonEvent *) &event)->y;
3878               if (event.type == EVENT_BUTTONPRESS)
3879                 button_status = ((ButtonEvent *) &event)->button;
3880               else
3881                 button_status = MB_RELEASED;
3882             }
3883
3884             /* this sets 'request_gadget_id' */
3885             HandleGadgets(mx, my, button_status);
3886
3887             switch (request_gadget_id)
3888             {
3889               case TOOL_CTRL_ID_YES:
3890                 result = TRUE;
3891                 break;
3892               case TOOL_CTRL_ID_NO:
3893                 result = FALSE;
3894                 break;
3895               case TOOL_CTRL_ID_CONFIRM:
3896                 result = TRUE | FALSE;
3897                 break;
3898
3899               case TOOL_CTRL_ID_PLAYER_1:
3900                 result = 1;
3901                 break;
3902               case TOOL_CTRL_ID_PLAYER_2:
3903                 result = 2;
3904                 break;
3905               case TOOL_CTRL_ID_PLAYER_3:
3906                 result = 3;
3907                 break;
3908               case TOOL_CTRL_ID_PLAYER_4:
3909                 result = 4;
3910                 break;
3911
3912               default:
3913                 break;
3914             }
3915
3916             break;
3917           }
3918
3919 #if defined(TARGET_SDL2)
3920           case SDL_WINDOWEVENT:
3921             HandleWindowEvent((WindowEvent *) &event);
3922             break;
3923
3924           case SDL_APP_WILLENTERBACKGROUND:
3925           case SDL_APP_DIDENTERBACKGROUND:
3926           case SDL_APP_WILLENTERFOREGROUND:
3927           case SDL_APP_DIDENTERFOREGROUND:
3928             HandlePauseResumeEvent((PauseResumeEvent *) &event);
3929             break;
3930 #endif
3931
3932           case EVENT_KEYPRESS:
3933           {
3934             Key key = GetEventKey((KeyEvent *)&event, TRUE);
3935
3936             switch (key)
3937             {
3938               case KSYM_space:
3939                 if (req_state & REQ_CONFIRM)
3940                   result = 1;
3941                 break;
3942
3943               case KSYM_Return:
3944 #if defined(TARGET_SDL2)
3945               case KSYM_Select:
3946               case KSYM_Menu:
3947 #if defined(KSYM_Rewind)
3948               case KSYM_Rewind:         /* for Amazon Fire TV remote */
3949 #endif
3950 #endif
3951                 result = 1;
3952                 break;
3953
3954               case KSYM_Escape:
3955 #if defined(TARGET_SDL2)
3956               case KSYM_Back:
3957 #if defined(KSYM_FastForward)
3958               case KSYM_FastForward:    /* for Amazon Fire TV remote */
3959 #endif
3960 #endif
3961                 result = 0;
3962                 break;
3963
3964               default:
3965                 HandleKeysDebug(key);
3966                 break;
3967             }
3968
3969             if (req_state & REQ_PLAYER)
3970               result = 0;
3971
3972             break;
3973           }
3974
3975           case EVENT_KEYRELEASE:
3976             ClearPlayerAction();
3977             break;
3978
3979 #if defined(TARGET_SDL2)
3980           case SDL_CONTROLLERBUTTONDOWN:
3981             switch (event.cbutton.button)
3982             {
3983               case SDL_CONTROLLER_BUTTON_A:
3984               case SDL_CONTROLLER_BUTTON_X:
3985               case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
3986                 result = 1;
3987                 break;
3988
3989               case SDL_CONTROLLER_BUTTON_B:
3990               case SDL_CONTROLLER_BUTTON_Y:
3991               case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
3992               case SDL_CONTROLLER_BUTTON_BACK:
3993                 result = 0;
3994                 break;
3995             }
3996
3997             if (req_state & REQ_PLAYER)
3998               result = 0;
3999
4000             break;
4001
4002           case SDL_CONTROLLERBUTTONUP:
4003             HandleJoystickEvent(&event);
4004             ClearPlayerAction();
4005             break;
4006 #endif
4007
4008           default:
4009             HandleOtherEvents(&event);
4010             break;
4011         }
4012       }
4013     }
4014     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4015     {
4016       int joy = AnyJoystick();
4017
4018       if (joy & JOY_BUTTON_1)
4019         result = 1;
4020       else if (joy & JOY_BUTTON_2)
4021         result = 0;
4022     }
4023
4024     if (level_solved)
4025     {
4026       if (global.use_envelope_request)
4027       {
4028         /* copy back current state of pressed buttons inside request area */
4029         BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4030       }
4031     }
4032
4033     BackToFront();
4034   }
4035
4036   return result;
4037 }
4038
4039 static boolean RequestDoor(char *text, unsigned int req_state)
4040 {
4041   unsigned int old_door_state;
4042   int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4043   int font_nr = FONT_TEXT_2;
4044   char *text_ptr;
4045   int result;
4046   int ty;
4047
4048   if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4049   {
4050     max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4051     font_nr = FONT_TEXT_1;
4052   }
4053
4054   if (game_status == GAME_MODE_PLAYING)
4055     BlitScreenToBitmap(backbuffer);
4056
4057   /* disable deactivated drawing when quick-loading level tape recording */
4058   if (tape.playing && tape.deactivate_display)
4059     TapeDeactivateDisplayOff(TRUE);
4060
4061   SetMouseCursor(CURSOR_DEFAULT);
4062
4063 #if defined(NETWORK_AVALIABLE)
4064   /* pause network game while waiting for request to answer */
4065   if (options.network &&
4066       game_status == GAME_MODE_PLAYING &&
4067       req_state & REQUEST_WAIT_FOR_INPUT)
4068     SendToServer_PausePlaying();
4069 #endif
4070
4071   old_door_state = GetDoorState();
4072
4073   /* simulate releasing mouse button over last gadget, if still pressed */
4074   if (button_status)
4075     HandleGadgets(-1, -1, 0);
4076
4077   UnmapAllGadgets();
4078
4079   /* draw released gadget before proceeding */
4080   // BackToFront();
4081
4082   if (old_door_state & DOOR_OPEN_1)
4083   {
4084     CloseDoor(DOOR_CLOSE_1);
4085
4086     /* save old door content */
4087     BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4088                0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4089   }
4090
4091   SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4092   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4093
4094   /* clear door drawing field */
4095   DrawBackground(DX, DY, DXSIZE, DYSIZE);
4096
4097   /* force DOOR font inside door area */
4098   SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4099
4100   /* write text for request */
4101   for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4102   {
4103     char text_line[max_request_line_len + 1];
4104     int tx, tl, tc = 0;
4105
4106     if (!*text_ptr)
4107       break;
4108
4109     for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4110     {
4111       tc = *(text_ptr + tx);
4112       // if (!tc || tc == ' ')
4113       if (!tc || tc == ' ' || tc == '?' || tc == '!')
4114         break;
4115     }
4116
4117     if ((tc == '?' || tc == '!') && tl == 0)
4118       tl = 1;
4119
4120     if (!tl)
4121     { 
4122       text_ptr++; 
4123       ty--; 
4124       continue; 
4125     }
4126
4127     strncpy(text_line, text_ptr, tl);
4128     text_line[tl] = 0;
4129
4130     DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4131              DY + 8 + ty * (getFontHeight(font_nr) + 2),
4132              text_line, font_nr);
4133
4134     text_ptr += tl + (tc == ' ' ? 1 : 0);
4135     // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4136   }
4137
4138   ResetFontStatus();
4139
4140   if (req_state & REQ_ASK)
4141   {
4142     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4143     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4144   }
4145   else if (req_state & REQ_CONFIRM)
4146   {
4147     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4148   }
4149   else if (req_state & REQ_PLAYER)
4150   {
4151     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4152     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4153     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4154     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4155   }
4156
4157   /* copy request gadgets to door backbuffer */
4158   BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4159
4160   OpenDoor(DOOR_OPEN_1);
4161
4162   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4163   {
4164     if (game_status == GAME_MODE_PLAYING)
4165     {
4166       SetPanelBackground();
4167       SetDrawBackgroundMask(REDRAW_DOOR_1);
4168     }
4169     else
4170     {
4171       SetDrawBackgroundMask(REDRAW_FIELD);
4172     }
4173
4174     return FALSE;
4175   }
4176
4177   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4178
4179   // ---------- handle request buttons ----------
4180   result = RequestHandleEvents(req_state);
4181
4182   UnmapToolButtons();
4183
4184   if (!(req_state & REQ_STAY_OPEN))
4185   {
4186     CloseDoor(DOOR_CLOSE_1);
4187
4188     if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4189         (req_state & REQ_REOPEN))
4190       OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4191   }
4192
4193   RemapAllGadgets();
4194
4195   if (game_status == GAME_MODE_PLAYING)
4196   {
4197     SetPanelBackground();
4198     SetDrawBackgroundMask(REDRAW_DOOR_1);
4199   }
4200   else
4201   {
4202     SetDrawBackgroundMask(REDRAW_FIELD);
4203   }
4204
4205 #if defined(NETWORK_AVALIABLE)
4206   /* continue network game after request */
4207   if (options.network &&
4208       game_status == GAME_MODE_PLAYING &&
4209       req_state & REQUEST_WAIT_FOR_INPUT)
4210     SendToServer_ContinuePlaying();
4211 #endif
4212
4213   /* restore deactivated drawing when quick-loading level tape recording */
4214   if (tape.playing && tape.deactivate_display)
4215     TapeDeactivateDisplayOn();
4216
4217   return result;
4218 }
4219
4220 static boolean RequestEnvelope(char *text, unsigned int req_state)
4221 {
4222   int result;
4223
4224   if (game_status == GAME_MODE_PLAYING)
4225     BlitScreenToBitmap(backbuffer);
4226
4227   /* disable deactivated drawing when quick-loading level tape recording */
4228   if (tape.playing && tape.deactivate_display)
4229     TapeDeactivateDisplayOff(TRUE);
4230
4231   SetMouseCursor(CURSOR_DEFAULT);
4232
4233 #if defined(NETWORK_AVALIABLE)
4234   /* pause network game while waiting for request to answer */
4235   if (options.network &&
4236       game_status == GAME_MODE_PLAYING &&
4237       req_state & REQUEST_WAIT_FOR_INPUT)
4238     SendToServer_PausePlaying();
4239 #endif
4240
4241   /* simulate releasing mouse button over last gadget, if still pressed */
4242   if (button_status)
4243     HandleGadgets(-1, -1, 0);
4244
4245   UnmapAllGadgets();
4246
4247   // (replace with setting corresponding request background)
4248   // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4249   // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4250
4251   /* clear door drawing field */
4252   // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4253
4254   ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4255
4256   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4257   {
4258     if (game_status == GAME_MODE_PLAYING)
4259     {
4260       SetPanelBackground();
4261       SetDrawBackgroundMask(REDRAW_DOOR_1);
4262     }
4263     else
4264     {
4265       SetDrawBackgroundMask(REDRAW_FIELD);
4266     }
4267
4268     return FALSE;
4269   }
4270
4271   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4272
4273   // ---------- handle request buttons ----------
4274   result = RequestHandleEvents(req_state);
4275
4276   UnmapToolButtons();
4277
4278   ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4279
4280   RemapAllGadgets();
4281
4282   if (game_status == GAME_MODE_PLAYING)
4283   {
4284     SetPanelBackground();
4285     SetDrawBackgroundMask(REDRAW_DOOR_1);
4286   }
4287   else
4288   {
4289     SetDrawBackgroundMask(REDRAW_FIELD);
4290   }
4291
4292 #if defined(NETWORK_AVALIABLE)
4293   /* continue network game after request */
4294   if (options.network &&
4295       game_status == GAME_MODE_PLAYING &&
4296       req_state & REQUEST_WAIT_FOR_INPUT)
4297     SendToServer_ContinuePlaying();
4298 #endif
4299
4300   /* restore deactivated drawing when quick-loading level tape recording */
4301   if (tape.playing && tape.deactivate_display)
4302     TapeDeactivateDisplayOn();
4303
4304   return result;
4305 }
4306
4307 boolean Request(char *text, unsigned int req_state)
4308 {
4309   boolean overlay_active = GetOverlayActive();
4310   boolean result;
4311
4312   SetOverlayActive(FALSE);
4313
4314   if (global.use_envelope_request)
4315     result = RequestEnvelope(text, req_state);
4316   else
4317     result = RequestDoor(text, req_state);
4318
4319   SetOverlayActive(overlay_active);
4320
4321   return result;
4322 }
4323
4324 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4325 {
4326   const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4327   const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4328   int compare_result;
4329
4330   if (dpo1->sort_priority != dpo2->sort_priority)
4331     compare_result = dpo1->sort_priority - dpo2->sort_priority;
4332   else
4333     compare_result = dpo1->nr - dpo2->nr;
4334
4335   return compare_result;
4336 }
4337
4338 void InitGraphicCompatibilityInfo_Doors()
4339 {
4340   struct
4341   {
4342     int door_token;
4343     int part_1, part_8;
4344     struct DoorInfo *door;
4345   }
4346   doors[] =
4347   {
4348     { DOOR_1,   IMG_GFX_DOOR_1_PART_1,  IMG_GFX_DOOR_1_PART_8,  &door_1 },
4349     { DOOR_2,   IMG_GFX_DOOR_2_PART_1,  IMG_GFX_DOOR_2_PART_8,  &door_2 },
4350
4351     { -1,       -1,                     -1,                     NULL    }
4352   };
4353   struct Rect door_rect_list[] =
4354   {
4355     { DX, DY, DXSIZE, DYSIZE },
4356     { VX, VY, VXSIZE, VYSIZE }
4357   };
4358   int i, j;
4359
4360   for (i = 0; doors[i].door_token != -1; i++)
4361   {
4362     int door_token = doors[i].door_token;
4363     int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4364     int part_1 = doors[i].part_1;
4365     int part_8 = doors[i].part_8;
4366     int part_2 = part_1 + 1;
4367     int part_3 = part_1 + 2;
4368     struct DoorInfo *door = doors[i].door;
4369     struct Rect *door_rect = &door_rect_list[door_index];
4370     boolean door_gfx_redefined = FALSE;
4371
4372     /* check if any door part graphic definitions have been redefined */
4373
4374     for (j = 0; door_part_controls[j].door_token != -1; j++)
4375     {
4376       struct DoorPartControlInfo *dpc = &door_part_controls[j];
4377       struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4378
4379       if (dpc->door_token == door_token && fi->redefined)
4380         door_gfx_redefined = TRUE;
4381     }
4382
4383     /* check for old-style door graphic/animation modifications */
4384
4385     if (!door_gfx_redefined)
4386     {
4387       if (door->anim_mode & ANIM_STATIC_PANEL)
4388       {
4389         door->panel.step_xoffset = 0;
4390         door->panel.step_yoffset = 0;
4391       }
4392
4393       if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4394       {
4395         struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4396         struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4397         int num_door_steps, num_panel_steps;
4398
4399         /* remove door part graphics other than the two default wings */
4400
4401         for (j = 0; door_part_controls[j].door_token != -1; j++)
4402         {
4403           struct DoorPartControlInfo *dpc = &door_part_controls[j];
4404           struct GraphicInfo *g = &graphic_info[dpc->graphic];
4405
4406           if (dpc->graphic >= part_3 &&
4407               dpc->graphic <= part_8)
4408             g->bitmap = NULL;
4409         }
4410
4411         /* set graphics and screen positions of the default wings */
4412
4413         g_part_1->width  = door_rect->width;
4414         g_part_1->height = door_rect->height;
4415         g_part_2->width  = door_rect->width;
4416         g_part_2->height = door_rect->height;
4417         g_part_2->src_x = door_rect->width;
4418         g_part_2->src_y = g_part_1->src_y;
4419
4420         door->part_2.x = door->part_1.x;
4421         door->part_2.y = door->part_1.y;
4422
4423         if (door->width != -1)
4424         {
4425           g_part_1->width = door->width;
4426           g_part_2->width = door->width;
4427
4428           // special treatment for graphics and screen position of right wing
4429           g_part_2->src_x += door_rect->width - door->width;
4430           door->part_2.x  += door_rect->width - door->width;
4431         }
4432
4433         if (door->height != -1)
4434         {
4435           g_part_1->height = door->height;
4436           g_part_2->height = door->height;
4437
4438           // special treatment for graphics and screen position of bottom wing
4439           g_part_2->src_y += door_rect->height - door->height;
4440           door->part_2.y  += door_rect->height - door->height;
4441         }
4442
4443         /* set animation delays for the default wings and panels */
4444
4445         door->part_1.step_delay = door->step_delay;
4446         door->part_2.step_delay = door->step_delay;
4447         door->panel.step_delay  = door->step_delay;
4448
4449         /* set animation draw order for the default wings */
4450
4451         door->part_1.sort_priority = 2; /* draw left wing over ... */
4452         door->part_2.sort_priority = 1; /*          ... right wing */
4453
4454         /* set animation draw offset for the default wings */
4455
4456         if (door->anim_mode & ANIM_HORIZONTAL)
4457         {
4458           door->part_1.step_xoffset = door->step_offset;
4459           door->part_1.step_yoffset = 0;
4460           door->part_2.step_xoffset = door->step_offset * -1;
4461           door->part_2.step_yoffset = 0;
4462
4463           num_door_steps = g_part_1->width / door->step_offset;
4464         }
4465         else    // ANIM_VERTICAL
4466         {
4467           door->part_1.step_xoffset = 0;
4468           door->part_1.step_yoffset = door->step_offset;
4469           door->part_2.step_xoffset = 0;
4470           door->part_2.step_yoffset = door->step_offset * -1;
4471
4472           num_door_steps = g_part_1->height / door->step_offset;
4473         }
4474
4475         /* set animation draw offset for the default panels */
4476
4477         if (door->step_offset > 1)
4478         {
4479           num_panel_steps = 2 * door_rect->height / door->step_offset;
4480           door->panel.start_step = num_panel_steps - num_door_steps;
4481           door->panel.start_step_closing = door->panel.start_step;
4482         }
4483         else
4484         {
4485           num_panel_steps = door_rect->height / door->step_offset;
4486           door->panel.start_step = num_panel_steps - num_door_steps / 2;
4487           door->panel.start_step_closing = door->panel.start_step;
4488           door->panel.step_delay *= 2;
4489         }
4490       }
4491     }
4492   }
4493 }
4494
4495 void InitDoors()
4496 {
4497   int i;
4498
4499   for (i = 0; door_part_controls[i].door_token != -1; i++)
4500   {
4501     struct DoorPartControlInfo *dpc = &door_part_controls[i];
4502     struct DoorPartOrderInfo *dpo = &door_part_order[i];
4503
4504     /* initialize "start_step_opening" and "start_step_closing", if needed */
4505     if (dpc->pos->start_step_opening == 0 &&
4506         dpc->pos->start_step_closing == 0)
4507     {
4508       // dpc->pos->start_step_opening = dpc->pos->start_step;
4509       dpc->pos->start_step_closing = dpc->pos->start_step;
4510     }
4511
4512     /* fill structure for door part draw order (sorted below) */
4513     dpo->nr = i;
4514     dpo->sort_priority = dpc->pos->sort_priority;
4515   }
4516
4517   /* sort door part controls according to sort_priority and graphic number */
4518   qsort(door_part_order, MAX_DOOR_PARTS,
4519         sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4520 }
4521
4522 unsigned int OpenDoor(unsigned int door_state)
4523 {
4524   if (door_state & DOOR_COPY_BACK)
4525   {
4526     if (door_state & DOOR_OPEN_1)
4527       BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4528                  1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4529
4530     if (door_state & DOOR_OPEN_2)
4531       BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4532                  1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4533
4534     door_state &= ~DOOR_COPY_BACK;
4535   }
4536
4537   return MoveDoor(door_state);
4538 }
4539
4540 unsigned int CloseDoor(unsigned int door_state)
4541 {
4542   unsigned int old_door_state = GetDoorState();
4543
4544   if (!(door_state & DOOR_NO_COPY_BACK))
4545   {
4546     if (old_door_state & DOOR_OPEN_1)
4547       BlitBitmap(backbuffer, bitmap_db_door_1,
4548                  DX, DY, DXSIZE, DYSIZE, 0, 0);
4549
4550     if (old_door_state & DOOR_OPEN_2)
4551       BlitBitmap(backbuffer, bitmap_db_door_2,
4552                  VX, VY, VXSIZE, VYSIZE, 0, 0);
4553
4554     door_state &= ~DOOR_NO_COPY_BACK;
4555   }
4556
4557   return MoveDoor(door_state);
4558 }
4559
4560 unsigned int GetDoorState()
4561 {
4562   return MoveDoor(DOOR_GET_STATE);
4563 }
4564
4565 unsigned int SetDoorState(unsigned int door_state)
4566 {
4567   return MoveDoor(door_state | DOOR_SET_STATE);
4568 }
4569
4570 int euclid(int a, int b)
4571 {
4572   return (b ? euclid(b, a % b) : a);
4573 }
4574
4575 unsigned int MoveDoor(unsigned int door_state)
4576 {
4577   struct Rect door_rect_list[] =
4578   {
4579     { DX, DY, DXSIZE, DYSIZE },
4580     { VX, VY, VXSIZE, VYSIZE }
4581   };
4582   static int door1 = DOOR_CLOSE_1;
4583   static int door2 = DOOR_CLOSE_2;
4584   unsigned int door_delay = 0;
4585   unsigned int door_delay_value;
4586   int i;
4587
4588   if (door_state == DOOR_GET_STATE)
4589     return (door1 | door2);
4590
4591   if (door_state & DOOR_SET_STATE)
4592   {
4593     if (door_state & DOOR_ACTION_1)
4594       door1 = door_state & DOOR_ACTION_1;
4595     if (door_state & DOOR_ACTION_2)
4596       door2 = door_state & DOOR_ACTION_2;
4597
4598     return (door1 | door2);
4599   }
4600
4601   if (!(door_state & DOOR_FORCE_REDRAW))
4602   {
4603     if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4604       door_state &= ~DOOR_OPEN_1;
4605     else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4606       door_state &= ~DOOR_CLOSE_1;
4607     if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4608       door_state &= ~DOOR_OPEN_2;
4609     else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4610       door_state &= ~DOOR_CLOSE_2;
4611   }
4612
4613   if (global.autoplay_leveldir)
4614   {
4615     door_state |= DOOR_NO_DELAY;
4616     door_state &= ~DOOR_CLOSE_ALL;
4617   }
4618
4619   if (game_status == GAME_MODE_EDITOR)
4620     door_state |= DOOR_NO_DELAY;
4621
4622   if (door_state & DOOR_ACTION)
4623   {
4624     boolean door_panel_drawn[NUM_DOORS];
4625     boolean panel_has_doors[NUM_DOORS];
4626     boolean door_part_skip[MAX_DOOR_PARTS];
4627     boolean door_part_done[MAX_DOOR_PARTS];
4628     boolean door_part_done_all;
4629     int num_steps[MAX_DOOR_PARTS];
4630     int max_move_delay = 0;     // delay for complete animations of all doors
4631     int max_step_delay = 0;     // delay (ms) between two animation frames
4632     int num_move_steps = 0;     // number of animation steps for all doors
4633     int max_move_delay_doors_only = 0;  // delay for doors only (no panel)
4634     int num_move_steps_doors_only = 0;  // steps for doors only (no panel)
4635     int current_move_delay = 0;
4636     int start = 0;
4637     int k;
4638
4639     for (i = 0; i < NUM_DOORS; i++)
4640       panel_has_doors[i] = FALSE;
4641
4642     for (i = 0; i < MAX_DOOR_PARTS; i++)
4643     {
4644       struct DoorPartControlInfo *dpc = &door_part_controls[i];
4645       struct GraphicInfo *g = &graphic_info[dpc->graphic];
4646       int door_token = dpc->door_token;
4647
4648       door_part_done[i] = FALSE;
4649       door_part_skip[i] = (!(door_state & door_token) ||
4650                            !g->bitmap);
4651     }
4652
4653     for (i = 0; i < MAX_DOOR_PARTS; i++)
4654     {
4655       int nr = door_part_order[i].nr;
4656       struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4657       struct DoorPartPosInfo *pos = dpc->pos;
4658       struct GraphicInfo *g = &graphic_info[dpc->graphic];
4659       int door_token = dpc->door_token;
4660       int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4661       boolean is_panel = DOOR_PART_IS_PANEL(nr);
4662       int step_xoffset = ABS(pos->step_xoffset);
4663       int step_yoffset = ABS(pos->step_yoffset);
4664       int step_delay = pos->step_delay;
4665       int current_door_state = door_state & door_token;
4666       boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
4667       boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4668       boolean part_opening = (is_panel ? door_closing : door_opening);
4669       int start_step = (part_opening ? pos->start_step_opening :
4670                         pos->start_step_closing);
4671       float move_xsize = (step_xoffset ? g->width  : 0);
4672       float move_ysize = (step_yoffset ? g->height : 0);
4673       int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4674       int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4675       int move_steps = (move_xsteps && move_ysteps ?
4676                         MIN(move_xsteps, move_ysteps) :
4677                         move_xsteps ? move_xsteps : move_ysteps) - start_step;
4678       int move_delay = move_steps * step_delay;
4679
4680       if (door_part_skip[nr])
4681         continue;
4682
4683       max_move_delay = MAX(max_move_delay, move_delay);
4684       max_step_delay = (max_step_delay == 0 ? step_delay :
4685                         euclid(max_step_delay, step_delay));
4686       num_steps[nr] = move_steps;
4687
4688       if (!is_panel)
4689       {
4690         max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4691
4692         panel_has_doors[door_index] = TRUE;
4693       }
4694     }
4695
4696     max_step_delay = MAX(1, max_step_delay);    // prevent division by zero
4697
4698     num_move_steps = max_move_delay / max_step_delay;
4699     num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4700
4701     door_delay_value = max_step_delay;
4702
4703     if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4704     {
4705       start = num_move_steps - 1;
4706     }
4707     else
4708     {
4709       /* opening door sound has priority over simultaneously closing door */
4710       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4711         PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4712       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4713         PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4714     }
4715
4716     for (k = start; k < num_move_steps; k++)
4717     {
4718       int last_frame = num_move_steps - 1;      // last frame of this "for" loop
4719
4720       door_part_done_all = TRUE;
4721
4722       for (i = 0; i < NUM_DOORS; i++)
4723         door_panel_drawn[i] = FALSE;
4724
4725       for (i = 0; i < MAX_DOOR_PARTS; i++)
4726       {
4727         int nr = door_part_order[i].nr;
4728         struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4729         struct DoorPartPosInfo *pos = dpc->pos;
4730         struct GraphicInfo *g = &graphic_info[dpc->graphic];
4731         int door_token = dpc->door_token;
4732         int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4733         boolean is_panel = DOOR_PART_IS_PANEL(nr);
4734         boolean is_panel_and_door_has_closed = FALSE;
4735         struct Rect *door_rect = &door_rect_list[door_index];
4736         Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4737                                   bitmap_db_door_2);
4738         Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4739         int current_door_state = door_state & door_token;
4740         boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
4741         boolean door_closing = !door_opening;
4742         boolean part_opening = (is_panel ? door_closing : door_opening);
4743         boolean part_closing = !part_opening;
4744         int start_step = (part_opening ? pos->start_step_opening :
4745                           pos->start_step_closing);
4746         int step_delay = pos->step_delay;
4747         int step_factor = step_delay / max_step_delay;
4748         int k1 = (step_factor ? k / step_factor + 1 : k);
4749         int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4750         int kk = MAX(0, k2);
4751         int g_src_x = 0;
4752         int g_src_y = 0;
4753         int src_x, src_y, src_xx, src_yy;
4754         int dst_x, dst_y, dst_xx, dst_yy;
4755         int width, height;
4756
4757         if (door_part_skip[nr])
4758           continue;
4759
4760         if (!(door_state & door_token))
4761           continue;
4762
4763         if (!g->bitmap)
4764           continue;
4765
4766         if (!is_panel)
4767         {
4768           int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4769           int kk_door = MAX(0, k2_door);
4770           int sync_frame = kk_door * door_delay_value;
4771           int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4772
4773           getFixedGraphicSource(dpc->graphic, frame, &bitmap,
4774                                 &g_src_x, &g_src_y);
4775         }
4776
4777         // draw door panel
4778
4779         if (!door_panel_drawn[door_index])
4780         {
4781           ClearRectangle(drawto, door_rect->x, door_rect->y,
4782                          door_rect->width, door_rect->height);
4783
4784           door_panel_drawn[door_index] = TRUE;
4785         }
4786
4787         // draw opening or closing door parts
4788
4789         if (pos->step_xoffset < 0)      // door part on right side
4790         {
4791           src_xx = 0;
4792           dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4793           width = g->width;
4794
4795           if (dst_xx + width > door_rect->width)
4796             width = door_rect->width - dst_xx;
4797         }
4798         else                            // door part on left side
4799         {
4800           src_xx = 0;
4801           dst_xx = pos->x - kk * pos->step_xoffset;
4802
4803           if (dst_xx < 0)
4804           {
4805             src_xx = ABS(dst_xx);
4806             dst_xx = 0;
4807           }
4808
4809           width = g->width - src_xx;
4810
4811           if (width > door_rect->width)
4812             width = door_rect->width;
4813
4814           // printf("::: k == %d [%d] \n", k, start_step);
4815         }
4816
4817         if (pos->step_yoffset < 0)      // door part on bottom side
4818         {
4819           src_yy = 0;
4820           dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4821           height = g->height;
4822
4823           if (dst_yy + height > door_rect->height)
4824             height = door_rect->height - dst_yy;
4825         }
4826         else                            // door part on top side
4827         {
4828           src_yy = 0;
4829           dst_yy = pos->y - kk * pos->step_yoffset;
4830
4831           if (dst_yy < 0)
4832           {
4833             src_yy = ABS(dst_yy);
4834             dst_yy = 0;
4835           }
4836
4837           height = g->height - src_yy;
4838         }
4839
4840         src_x = g_src_x + src_xx;
4841         src_y = g_src_y + src_yy;
4842
4843         dst_x = door_rect->x + dst_xx;
4844         dst_y = door_rect->y + dst_yy;
4845
4846         is_panel_and_door_has_closed =
4847           (is_panel &&
4848            door_closing &&
4849            panel_has_doors[door_index] &&
4850            k >= num_move_steps_doors_only - 1);
4851
4852         if (width  >= 0 && width  <= g->width &&
4853             height >= 0 && height <= g->height &&
4854             !is_panel_and_door_has_closed)
4855         {
4856           if (is_panel || !pos->draw_masked)
4857             BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4858                        dst_x, dst_y);
4859           else
4860             BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4861                              dst_x, dst_y);
4862         }
4863
4864         redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4865
4866         if ((part_opening && (width < 0         || height < 0)) ||
4867             (part_closing && (width >= g->width && height >= g->height)))
4868           door_part_done[nr] = TRUE;
4869
4870         // continue door part animations, but not panel after door has closed
4871         if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4872           door_part_done_all = FALSE;
4873       }
4874
4875       if (!(door_state & DOOR_NO_DELAY))
4876       {
4877         BackToFront();
4878
4879         SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4880
4881         current_move_delay += max_step_delay;
4882
4883         /* prevent OS (Windows) from complaining about program not responding */
4884         CheckQuitEvent();
4885       }
4886
4887       if (door_part_done_all)
4888         break;
4889     }
4890   }
4891
4892   if (door_state & DOOR_ACTION_1)
4893     door1 = door_state & DOOR_ACTION_1;
4894   if (door_state & DOOR_ACTION_2)
4895     door2 = door_state & DOOR_ACTION_2;
4896
4897   // draw masked border over door area
4898   DrawMaskedBorder(REDRAW_DOOR_1);
4899   DrawMaskedBorder(REDRAW_DOOR_2);
4900
4901   return (door1 | door2);
4902 }
4903
4904 static boolean useSpecialEditorDoor()
4905 {
4906   int graphic = IMG_GLOBAL_BORDER_EDITOR;
4907   boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4908
4909   // do not draw special editor door if editor border defined or redefined
4910   if (graphic_info[graphic].bitmap != NULL || redefined)
4911     return FALSE;
4912
4913   // do not draw special editor door if global border defined to be empty
4914   if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4915     return FALSE;
4916
4917   // do not draw special editor door if viewport definitions do not match
4918   if (EX != VX ||
4919       EY >= VY ||
4920       EXSIZE != VXSIZE ||
4921       EY + EYSIZE != VY + VYSIZE)
4922     return FALSE;
4923
4924   return TRUE;
4925 }
4926
4927 void DrawSpecialEditorDoor()
4928 {
4929   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4930   int top_border_width = gfx1->width;
4931   int top_border_height = gfx1->height;
4932   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4933   int ex = EX - outer_border;
4934   int ey = EY - outer_border;
4935   int vy = VY - outer_border;
4936   int exsize = EXSIZE + 2 * outer_border;
4937
4938   if (!useSpecialEditorDoor())
4939     return;
4940
4941   /* draw bigger level editor toolbox window */
4942   BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4943              top_border_width, top_border_height, ex, ey - top_border_height);
4944   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4945              exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4946
4947   redraw_mask |= REDRAW_ALL;
4948 }
4949
4950 void UndrawSpecialEditorDoor()
4951 {
4952   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4953   int top_border_width = gfx1->width;
4954   int top_border_height = gfx1->height;
4955   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4956   int ex = EX - outer_border;
4957   int ey = EY - outer_border;
4958   int ey_top = ey - top_border_height;
4959   int exsize = EXSIZE + 2 * outer_border;
4960   int eysize = EYSIZE + 2 * outer_border;
4961
4962   if (!useSpecialEditorDoor())
4963     return;
4964
4965   /* draw normal tape recorder window */
4966   if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4967   {
4968     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4969                ex, ey_top, top_border_width, top_border_height,
4970                ex, ey_top);
4971     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4972                ex, ey, exsize, eysize, ex, ey);
4973   }
4974   else
4975   {
4976     // if screen background is set to "[NONE]", clear editor toolbox window
4977     ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4978     ClearRectangle(drawto, ex, ey, exsize, eysize);
4979   }
4980
4981   redraw_mask |= REDRAW_ALL;
4982 }
4983
4984
4985 /* ---------- new tool button stuff ---------------------------------------- */
4986
4987 static struct
4988 {
4989   int graphic;
4990   struct TextPosInfo *pos;
4991   int gadget_id;
4992   char *infotext;
4993 } toolbutton_info[NUM_TOOL_BUTTONS] =
4994 {
4995   {
4996     IMG_GFX_REQUEST_BUTTON_YES,         &request.button.yes,
4997     TOOL_CTRL_ID_YES,                   "yes"
4998   },
4999   {
5000     IMG_GFX_REQUEST_BUTTON_NO,          &request.button.no,
5001     TOOL_CTRL_ID_NO,                    "no"
5002   },
5003   {
5004     IMG_GFX_REQUEST_BUTTON_CONFIRM,     &request.button.confirm,
5005     TOOL_CTRL_ID_CONFIRM,               "confirm"
5006   },
5007   {
5008     IMG_GFX_REQUEST_BUTTON_PLAYER_1,    &request.button.player_1,
5009     TOOL_CTRL_ID_PLAYER_1,              "player 1"
5010   },
5011   {
5012     IMG_GFX_REQUEST_BUTTON_PLAYER_2,    &request.button.player_2,
5013     TOOL_CTRL_ID_PLAYER_2,              "player 2"
5014   },
5015   {
5016     IMG_GFX_REQUEST_BUTTON_PLAYER_3,    &request.button.player_3,
5017     TOOL_CTRL_ID_PLAYER_3,              "player 3"
5018   },
5019   {
5020     IMG_GFX_REQUEST_BUTTON_PLAYER_4,    &request.button.player_4,
5021     TOOL_CTRL_ID_PLAYER_4,              "player 4"
5022   }
5023 };
5024
5025 void CreateToolButtons()
5026 {
5027   int i;
5028
5029   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5030   {
5031     struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
5032     struct TextPosInfo *pos = toolbutton_info[i].pos;
5033     struct GadgetInfo *gi;
5034     Bitmap *deco_bitmap = None;
5035     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5036     unsigned int event_mask = GD_EVENT_RELEASED;
5037     int dx = DX;
5038     int dy = DY;
5039     int gd_x = gfx->src_x;
5040     int gd_y = gfx->src_y;
5041     int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5042     int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5043     int id = i;
5044
5045     if (global.use_envelope_request)
5046       setRequestPosition(&dx, &dy, TRUE);
5047
5048     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5049     {
5050       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5051
5052       getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5053                             pos->size, &deco_bitmap, &deco_x, &deco_y);
5054       deco_xpos = (gfx->width  - pos->size) / 2;
5055       deco_ypos = (gfx->height - pos->size) / 2;
5056     }
5057
5058     gi = CreateGadget(GDI_CUSTOM_ID, id,
5059                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
5060                       GDI_X, dx + GDI_ACTIVE_POS(pos->x),
5061                       GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
5062                       GDI_WIDTH, gfx->width,
5063                       GDI_HEIGHT, gfx->height,
5064                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5065                       GDI_STATE, GD_BUTTON_UNPRESSED,
5066                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5067                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5068                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5069                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5070                       GDI_DECORATION_SIZE, pos->size, pos->size,
5071                       GDI_DECORATION_SHIFTING, 1, 1,
5072                       GDI_DIRECT_DRAW, FALSE,
5073                       GDI_EVENT_MASK, event_mask,
5074                       GDI_CALLBACK_ACTION, HandleToolButtons,
5075                       GDI_END);
5076
5077     if (gi == NULL)
5078       Error(ERR_EXIT, "cannot create gadget");
5079
5080     tool_gadget[id] = gi;
5081   }
5082 }
5083
5084 void FreeToolButtons()
5085 {
5086   int i;
5087
5088   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5089     FreeGadget(tool_gadget[i]);
5090 }
5091
5092 static void UnmapToolButtons()
5093 {
5094   int i;
5095
5096   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5097     UnmapGadget(tool_gadget[i]);
5098 }
5099
5100 static void HandleToolButtons(struct GadgetInfo *gi)
5101 {
5102   request_gadget_id = gi->custom_id;
5103 }
5104
5105 static struct Mapping_EM_to_RND_object
5106 {
5107   int element_em;
5108   boolean is_rnd_to_em_mapping;         /* unique mapping EM <-> RND */
5109   boolean is_backside;                  /* backside of moving element */
5110
5111   int element_rnd;
5112   int action;
5113   int direction;
5114 }
5115 em_object_mapping_list[] =
5116 {
5117   {
5118     Xblank,                             TRUE,   FALSE,
5119     EL_EMPTY,                           -1, -1
5120   },
5121   {
5122     Yacid_splash_eB,                    FALSE,  FALSE,
5123     EL_ACID_SPLASH_RIGHT,               -1, -1
5124   },
5125   {
5126     Yacid_splash_wB,                    FALSE,  FALSE,
5127     EL_ACID_SPLASH_LEFT,                -1, -1
5128   },
5129
5130 #ifdef EM_ENGINE_BAD_ROLL
5131   {
5132     Xstone_force_e,                     FALSE,  FALSE,
5133     EL_ROCK,                            -1, MV_BIT_RIGHT
5134   },
5135   {
5136     Xstone_force_w,                     FALSE,  FALSE,
5137     EL_ROCK,                            -1, MV_BIT_LEFT
5138   },
5139   {
5140     Xnut_force_e,                       FALSE,  FALSE,
5141     EL_NUT,                             -1, MV_BIT_RIGHT
5142   },
5143   {
5144     Xnut_force_w,                       FALSE,  FALSE,
5145     EL_NUT,                             -1, MV_BIT_LEFT
5146   },
5147   {
5148     Xspring_force_e,                    FALSE,  FALSE,
5149     EL_SPRING,                          -1, MV_BIT_RIGHT
5150   },
5151   {
5152     Xspring_force_w,                    FALSE,  FALSE,
5153     EL_SPRING,                          -1, MV_BIT_LEFT
5154   },
5155   {
5156     Xemerald_force_e,                   FALSE,  FALSE,
5157     EL_EMERALD,                         -1, MV_BIT_RIGHT
5158   },
5159   {
5160     Xemerald_force_w,                   FALSE,  FALSE,
5161     EL_EMERALD,                         -1, MV_BIT_LEFT
5162   },
5163   {
5164     Xdiamond_force_e,                   FALSE,  FALSE,
5165     EL_DIAMOND,                         -1, MV_BIT_RIGHT
5166   },
5167   {
5168     Xdiamond_force_w,                   FALSE,  FALSE,
5169     EL_DIAMOND,                         -1, MV_BIT_LEFT
5170   },
5171   {
5172     Xbomb_force_e,                      FALSE,  FALSE,
5173     EL_BOMB,                            -1, MV_BIT_RIGHT
5174   },
5175   {
5176     Xbomb_force_w,                      FALSE,  FALSE,
5177     EL_BOMB,                            -1, MV_BIT_LEFT
5178   },
5179 #endif  /* EM_ENGINE_BAD_ROLL */
5180
5181   {
5182     Xstone,                             TRUE,   FALSE,
5183     EL_ROCK,                            -1, -1
5184   },
5185   {
5186     Xstone_pause,                       FALSE,  FALSE,
5187     EL_ROCK,                            -1, -1
5188   },
5189   {
5190     Xstone_fall,                        FALSE,  FALSE,
5191     EL_ROCK,                            -1, -1
5192   },
5193   {
5194     Ystone_s,                           FALSE,  FALSE,
5195     EL_ROCK,                            ACTION_FALLING, -1
5196   },
5197   {
5198     Ystone_sB,                          FALSE,  TRUE,
5199     EL_ROCK,                            ACTION_FALLING, -1
5200   },
5201   {
5202     Ystone_e,                           FALSE,  FALSE,
5203     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
5204   },
5205   {
5206     Ystone_eB,                          FALSE,  TRUE,
5207     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
5208   },
5209   {
5210     Ystone_w,                           FALSE,  FALSE,
5211     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
5212   },
5213   {
5214     Ystone_wB,                          FALSE,  TRUE,
5215     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
5216   },
5217   {
5218     Xnut,                               TRUE,   FALSE,
5219     EL_NUT,                             -1, -1
5220   },
5221   {
5222     Xnut_pause,                         FALSE,  FALSE,
5223     EL_NUT,                             -1, -1
5224   },
5225   {
5226     Xnut_fall,                          FALSE,  FALSE,
5227     EL_NUT,                             -1, -1
5228   },
5229   {
5230     Ynut_s,                             FALSE,  FALSE,
5231     EL_NUT,                             ACTION_FALLING, -1
5232   },
5233   {
5234     Ynut_sB,                            FALSE,  TRUE,
5235     EL_NUT,                             ACTION_FALLING, -1
5236   },
5237   {
5238     Ynut_e,                             FALSE,  FALSE,
5239     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
5240   },
5241   {
5242     Ynut_eB,                            FALSE,  TRUE,
5243     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
5244   },
5245   {
5246     Ynut_w,                             FALSE,  FALSE,
5247     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
5248   },
5249   {
5250     Ynut_wB,                            FALSE,  TRUE,
5251     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
5252   },
5253   {
5254     Xbug_n,                             TRUE,   FALSE,
5255     EL_BUG_UP,                          -1, -1
5256   },
5257   {
5258     Xbug_e,                             TRUE,   FALSE,
5259     EL_BUG_RIGHT,                       -1, -1
5260   },
5261   {
5262     Xbug_s,                             TRUE,   FALSE,
5263     EL_BUG_DOWN,                        -1, -1
5264   },
5265   {
5266     Xbug_w,                             TRUE,   FALSE,
5267     EL_BUG_LEFT,                        -1, -1
5268   },
5269   {
5270     Xbug_gon,                           FALSE,  FALSE,
5271     EL_BUG_UP,                          -1, -1
5272   },
5273   {
5274     Xbug_goe,                           FALSE,  FALSE,
5275     EL_BUG_RIGHT,                       -1, -1
5276   },
5277   {
5278     Xbug_gos,                           FALSE,  FALSE,
5279     EL_BUG_DOWN,                        -1, -1
5280   },
5281   {
5282     Xbug_gow,                           FALSE,  FALSE,
5283     EL_BUG_LEFT,                        -1, -1
5284   },
5285   {
5286     Ybug_n,                             FALSE,  FALSE,
5287     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
5288   },
5289   {
5290     Ybug_nB,                            FALSE,  TRUE,
5291     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
5292   },
5293   {
5294     Ybug_e,                             FALSE,  FALSE,
5295     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
5296   },
5297   {
5298     Ybug_eB,                            FALSE,  TRUE,
5299     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
5300   },
5301   {
5302     Ybug_s,                             FALSE,  FALSE,
5303     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
5304   },
5305   {
5306     Ybug_sB,                            FALSE,  TRUE,
5307     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
5308   },
5309   {
5310     Ybug_w,                             FALSE,  FALSE,
5311     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
5312   },
5313   {
5314     Ybug_wB,                            FALSE,  TRUE,
5315     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
5316   },
5317   {
5318     Ybug_w_n,                           FALSE,  FALSE,
5319     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5320   },
5321   {
5322     Ybug_n_e,                           FALSE,  FALSE,
5323     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5324   },
5325   {
5326     Ybug_e_s,                           FALSE,  FALSE,
5327     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5328   },
5329   {
5330     Ybug_s_w,                           FALSE,  FALSE,
5331     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5332   },
5333   {
5334     Ybug_e_n,                           FALSE,  FALSE,
5335     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5336   },
5337   {
5338     Ybug_s_e,                           FALSE,  FALSE,
5339     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5340   },
5341   {
5342     Ybug_w_s,                           FALSE,  FALSE,
5343     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5344   },
5345   {
5346     Ybug_n_w,                           FALSE,  FALSE,
5347     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5348   },
5349   {
5350     Ybug_stone,                         FALSE,  FALSE,
5351     EL_BUG,                             ACTION_SMASHED_BY_ROCK, -1
5352   },
5353   {
5354     Ybug_spring,                        FALSE,  FALSE,
5355     EL_BUG,                             ACTION_SMASHED_BY_SPRING, -1
5356   },
5357   {
5358     Xtank_n,                            TRUE,   FALSE,
5359     EL_SPACESHIP_UP,                    -1, -1
5360   },
5361   {
5362     Xtank_e,                            TRUE,   FALSE,
5363     EL_SPACESHIP_RIGHT,                 -1, -1
5364   },
5365   {
5366     Xtank_s,                            TRUE,   FALSE,
5367     EL_SPACESHIP_DOWN,                  -1, -1
5368   },
5369   {
5370     Xtank_w,                            TRUE,   FALSE,
5371     EL_SPACESHIP_LEFT,                  -1, -1
5372   },
5373   {
5374     Xtank_gon,                          FALSE,  FALSE,
5375     EL_SPACESHIP_UP,                    -1, -1
5376   },
5377   {
5378     Xtank_goe,                          FALSE,  FALSE,
5379     EL_SPACESHIP_RIGHT,                 -1, -1
5380   },
5381   {
5382     Xtank_gos,                          FALSE,  FALSE,
5383     EL_SPACESHIP_DOWN,                  -1, -1
5384   },
5385   {
5386     Xtank_gow,                          FALSE,  FALSE,
5387     EL_SPACESHIP_LEFT,                  -1, -1
5388   },
5389   {
5390     Ytank_n,                            FALSE,  FALSE,
5391     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
5392   },
5393   {
5394     Ytank_nB,                           FALSE,  TRUE,
5395     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
5396   },
5397   {
5398     Ytank_e,                            FALSE,  FALSE,
5399     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
5400   },
5401   {
5402     Ytank_eB,                           FALSE,  TRUE,
5403     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
5404   },
5405   {
5406     Ytank_s,                            FALSE,  FALSE,
5407     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
5408   },
5409   {
5410     Ytank_sB,                           FALSE,  TRUE,
5411     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
5412   },
5413   {
5414     Ytank_w,                            FALSE,  FALSE,
5415     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
5416   },
5417   {
5418     Ytank_wB,                           FALSE,  TRUE,
5419     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
5420   },
5421   {
5422     Ytank_w_n,                          FALSE,  FALSE,
5423     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5424   },
5425   {
5426     Ytank_n_e,                          FALSE,  FALSE,
5427     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5428   },
5429   {
5430     Ytank_e_s,                          FALSE,  FALSE,
5431     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5432   },
5433   {
5434     Ytank_s_w,                          FALSE,  FALSE,
5435     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5436   },
5437   {
5438     Ytank_e_n,                          FALSE,  FALSE,
5439     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5440   },
5441   {
5442     Ytank_s_e,                          FALSE,  FALSE,
5443     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5444   },
5445   {
5446     Ytank_w_s,                          FALSE,  FALSE,
5447     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5448   },
5449   {
5450     Ytank_n_w,                          FALSE,  FALSE,
5451     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5452   },
5453   {
5454     Ytank_stone,                        FALSE,  FALSE,
5455     EL_SPACESHIP,                       ACTION_SMASHED_BY_ROCK, -1
5456   },
5457   {
5458     Ytank_spring,                       FALSE,  FALSE,
5459     EL_SPACESHIP,                       ACTION_SMASHED_BY_SPRING, -1
5460   },
5461   {
5462     Xandroid,                           TRUE,   FALSE,
5463     EL_EMC_ANDROID,                     ACTION_ACTIVE, -1
5464   },
5465   {
5466     Xandroid_1_n,                       FALSE,  FALSE,
5467     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
5468   },
5469   {
5470     Xandroid_2_n,                       FALSE,  FALSE,
5471     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
5472   },
5473   {
5474     Xandroid_1_e,                       FALSE,  FALSE,
5475     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
5476   },
5477   {
5478     Xandroid_2_e,                       FALSE,  FALSE,
5479     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
5480   },
5481   {
5482     Xandroid_1_w,                       FALSE,  FALSE,
5483     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
5484   },
5485   {
5486     Xandroid_2_w,                       FALSE,  FALSE,
5487     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
5488   },
5489   {
5490     Xandroid_1_s,                       FALSE,  FALSE,
5491     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
5492   },
5493   {
5494     Xandroid_2_s,                       FALSE,  FALSE,
5495     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
5496   },
5497   {
5498     Yandroid_n,                         FALSE,  FALSE,
5499     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
5500   },
5501   {
5502     Yandroid_nB,                        FALSE,  TRUE,
5503     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
5504   },
5505   {
5506     Yandroid_ne,                        FALSE,  FALSE,
5507     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPRIGHT
5508   },
5509   {
5510     Yandroid_neB,                       FALSE,  TRUE,
5511     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPRIGHT
5512   },
5513   {
5514     Yandroid_e,                         FALSE,  FALSE,
5515     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
5516   },
5517   {
5518     Yandroid_eB,                        FALSE,  TRUE,
5519     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
5520   },
5521   {
5522     Yandroid_se,                        FALSE,  FALSE,
5523     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNRIGHT
5524   },
5525   {
5526     Yandroid_seB,                       FALSE,  TRUE,
5527     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5528   },
5529   {
5530     Yandroid_s,                         FALSE,  FALSE,
5531     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
5532   },
5533   {
5534     Yandroid_sB,                        FALSE,  TRUE,
5535     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
5536   },
5537   {
5538     Yandroid_sw,                        FALSE,  FALSE,
5539     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNLEFT
5540   },
5541   {
5542     Yandroid_swB,                       FALSE,  TRUE,
5543     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNLEFT
5544   },
5545   {
5546     Yandroid_w,                         FALSE,  FALSE,
5547     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
5548   },
5549   {
5550     Yandroid_wB,                        FALSE,  TRUE,
5551     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
5552   },
5553   {
5554     Yandroid_nw,                        FALSE,  FALSE,
5555     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPLEFT
5556   },
5557   {
5558     Yandroid_nwB,                       FALSE,  TRUE,
5559     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPLEFT
5560   },
5561   {
5562     Xspring,                            TRUE,   FALSE,
5563     EL_SPRING,                          -1, -1
5564   },
5565   {
5566     Xspring_pause,                      FALSE,  FALSE,
5567     EL_SPRING,                          -1, -1
5568   },
5569   {
5570     Xspring_e,                          FALSE,  FALSE,
5571     EL_SPRING,                          -1, -1
5572   },
5573   {
5574     Xspring_w,                          FALSE,  FALSE,
5575     EL_SPRING,                          -1, -1
5576   },
5577   {
5578     Xspring_fall,                       FALSE,  FALSE,
5579     EL_SPRING,                          -1, -1
5580   },
5581   {
5582     Yspring_s,                          FALSE,  FALSE,
5583     EL_SPRING,                          ACTION_FALLING, -1
5584   },
5585   {
5586     Yspring_sB,                         FALSE,  TRUE,
5587     EL_SPRING,                          ACTION_FALLING, -1
5588   },
5589   {
5590     Yspring_e,                          FALSE,  FALSE,
5591     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
5592   },
5593   {
5594     Yspring_eB,                         FALSE,  TRUE,
5595     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
5596   },
5597   {
5598     Yspring_w,                          FALSE,  FALSE,
5599     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
5600   },
5601   {
5602     Yspring_wB,                         FALSE,  TRUE,
5603     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
5604   },
5605   {
5606     Yspring_kill_e,                     FALSE,  FALSE,
5607     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
5608   },
5609   {
5610     Yspring_kill_eB,                    FALSE,  TRUE,
5611     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
5612   },
5613   {
5614     Yspring_kill_w,                     FALSE,  FALSE,
5615     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
5616   },
5617   {
5618     Yspring_kill_wB,                    FALSE,  TRUE,
5619     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
5620   },
5621   {
5622     Xeater_n,                           TRUE,   FALSE,
5623     EL_YAMYAM_UP,                       -1, -1
5624   },
5625   {
5626     Xeater_e,                           TRUE,   FALSE,
5627     EL_YAMYAM_RIGHT,                    -1, -1
5628   },
5629   {
5630     Xeater_w,                           TRUE,   FALSE,
5631     EL_YAMYAM_LEFT,                     -1, -1
5632   },
5633   {
5634     Xeater_s,                           TRUE,   FALSE,
5635     EL_YAMYAM_DOWN,                     -1, -1
5636   },
5637   {
5638     Yeater_n,                           FALSE,  FALSE,
5639     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
5640   },
5641   {
5642     Yeater_nB,                          FALSE,  TRUE,
5643     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
5644   },
5645   {
5646     Yeater_e,                           FALSE,  FALSE,
5647     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
5648   },
5649   {
5650     Yeater_eB,                          FALSE,  TRUE,
5651     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
5652   },
5653   {
5654     Yeater_s,                           FALSE,  FALSE,
5655     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
5656   },
5657   {
5658     Yeater_sB,                          FALSE,  TRUE,
5659     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
5660   },
5661   {
5662     Yeater_w,                           FALSE,  FALSE,
5663     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
5664   },
5665   {
5666     Yeater_wB,                          FALSE,  TRUE,
5667     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
5668   },
5669   {
5670     Yeater_stone,                       FALSE,  FALSE,
5671     EL_YAMYAM,                          ACTION_SMASHED_BY_ROCK, -1
5672   },
5673   {
5674     Yeater_spring,                      FALSE,  FALSE,
5675     EL_YAMYAM,                          ACTION_SMASHED_BY_SPRING, -1
5676   },
5677   {
5678     Xalien,                             TRUE,   FALSE,
5679     EL_ROBOT,                           -1, -1
5680   },
5681   {
5682     Xalien_pause,                       FALSE,  FALSE,
5683     EL_ROBOT,                           -1, -1
5684   },
5685   {
5686     Yalien_n,                           FALSE,  FALSE,
5687     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
5688   },
5689   {
5690     Yalien_nB,                          FALSE,  TRUE,
5691     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
5692   },
5693   {
5694     Yalien_e,                           FALSE,  FALSE,
5695     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
5696   },
5697   {
5698     Yalien_eB,                          FALSE,  TRUE,
5699     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
5700   },
5701   {
5702     Yalien_s,                           FALSE,  FALSE,
5703     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
5704   },
5705   {
5706     Yalien_sB,                          FALSE,  TRUE,
5707     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
5708   },
5709   {
5710     Yalien_w,                           FALSE,  FALSE,
5711     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
5712   },
5713   {
5714     Yalien_wB,                          FALSE,  TRUE,
5715     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
5716   },
5717   {
5718     Yalien_stone,                       FALSE,  FALSE,
5719     EL_ROBOT,                           ACTION_SMASHED_BY_ROCK, -1
5720   },
5721   {
5722     Yalien_spring,                      FALSE,  FALSE,
5723     EL_ROBOT,                           ACTION_SMASHED_BY_SPRING, -1
5724   },
5725   {
5726     Xemerald,                           TRUE,   FALSE,
5727     EL_EMERALD,                         -1, -1
5728   },
5729   {
5730     Xemerald_pause,                     FALSE,  FALSE,
5731     EL_EMERALD,                         -1, -1
5732   },
5733   {
5734     Xemerald_fall,                      FALSE,  FALSE,
5735     EL_EMERALD,                         -1, -1
5736   },
5737   {
5738     Xemerald_shine,                     FALSE,  FALSE,
5739     EL_EMERALD,                         ACTION_TWINKLING, -1
5740   },
5741   {
5742     Yemerald_s,                         FALSE,  FALSE,
5743     EL_EMERALD,                         ACTION_FALLING, -1
5744   },
5745   {
5746     Yemerald_sB,                        FALSE,  TRUE,
5747     EL_EMERALD,                         ACTION_FALLING, -1
5748   },
5749   {
5750     Yemerald_e,                         FALSE,  FALSE,
5751     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
5752   },
5753   {
5754     Yemerald_eB,                        FALSE,  TRUE,
5755     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
5756   },
5757   {
5758     Yemerald_w,                         FALSE,  FALSE,
5759     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
5760   },
5761   {
5762     Yemerald_wB,                        FALSE,  TRUE,
5763     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
5764   },
5765   {
5766     Yemerald_eat,                       FALSE,  FALSE,
5767     EL_EMERALD,                         ACTION_COLLECTING, -1
5768   },
5769   {
5770     Yemerald_stone,                     FALSE,  FALSE,
5771     EL_NUT,                             ACTION_BREAKING, -1
5772   },
5773   {
5774     Xdiamond,                           TRUE,   FALSE,
5775     EL_DIAMOND,                         -1, -1
5776   },
5777   {
5778     Xdiamond_pause,                     FALSE,  FALSE,
5779     EL_DIAMOND,                         -1, -1
5780   },
5781   {
5782     Xdiamond_fall,                      FALSE,  FALSE,
5783     EL_DIAMOND,                         -1, -1
5784   },
5785   {
5786     Xdiamond_shine,                     FALSE,  FALSE,
5787     EL_DIAMOND,                         ACTION_TWINKLING, -1
5788   },
5789   {
5790     Ydiamond_s,                         FALSE,  FALSE,
5791     EL_DIAMOND,                         ACTION_FALLING, -1
5792   },
5793   {
5794     Ydiamond_sB,                        FALSE,  TRUE,
5795     EL_DIAMOND,                         ACTION_FALLING, -1
5796   },
5797   {
5798     Ydiamond_e,                         FALSE,  FALSE,
5799     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
5800   },
5801   {
5802     Ydiamond_eB,                        FALSE,  TRUE,
5803     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
5804   },
5805   {
5806     Ydiamond_w,                         FALSE,  FALSE,
5807     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
5808   },
5809   {
5810     Ydiamond_wB,                        FALSE,  TRUE,
5811     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
5812   },
5813   {
5814     Ydiamond_eat,                       FALSE,  FALSE,
5815     EL_DIAMOND,                         ACTION_COLLECTING, -1
5816   },
5817   {
5818     Ydiamond_stone,                     FALSE,  FALSE,
5819     EL_DIAMOND,                         ACTION_SMASHED_BY_ROCK, -1
5820   },
5821   {
5822     Xdrip_fall,                         TRUE,   FALSE,
5823     EL_AMOEBA_DROP,                     -1, -1
5824   },
5825   {
5826     Xdrip_stretch,                      FALSE,  FALSE,
5827     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5828   },
5829   {
5830     Xdrip_stretchB,                     FALSE,  TRUE,
5831     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5832   },
5833   {
5834     Xdrip_eat,                          FALSE,  FALSE,
5835     EL_AMOEBA_DROP,                     ACTION_GROWING, -1
5836   },
5837   {
5838     Ydrip_s1,                           FALSE,  FALSE,
5839     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5840   },
5841   {
5842     Ydrip_s1B,                          FALSE,  TRUE,
5843     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5844   },
5845   {
5846     Ydrip_s2,                           FALSE,  FALSE,
5847     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5848   },
5849   {
5850     Ydrip_s2B,                          FALSE,  TRUE,
5851     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
5852   },
5853   {
5854     Xbomb,                              TRUE,   FALSE,
5855     EL_BOMB,                            -1, -1
5856   },
5857   {
5858     Xbomb_pause,                        FALSE,  FALSE,
5859     EL_BOMB,                            -1, -1
5860   },
5861   {
5862     Xbomb_fall,                         FALSE,  FALSE,
5863     EL_BOMB,                            -1, -1
5864   },
5865   {
5866     Ybomb_s,                            FALSE,  FALSE,
5867     EL_BOMB,                            ACTION_FALLING, -1
5868   },
5869   {
5870     Ybomb_sB,                           FALSE,  TRUE,
5871     EL_BOMB,                            ACTION_FALLING, -1
5872   },
5873   {
5874     Ybomb_e,                            FALSE,  FALSE,
5875     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
5876   },
5877   {
5878     Ybomb_eB,                           FALSE,  TRUE,
5879     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
5880   },
5881   {
5882     Ybomb_w,                            FALSE,  FALSE,
5883     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
5884   },
5885   {
5886     Ybomb_wB,                           FALSE,  TRUE,
5887     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
5888   },
5889   {
5890     Ybomb_eat,                          FALSE,  FALSE,
5891     EL_BOMB,                            ACTION_ACTIVATING, -1
5892   },
5893   {
5894     Xballoon,                           TRUE,   FALSE,
5895     EL_BALLOON,                         -1, -1
5896   },
5897   {
5898     Yballoon_n,                         FALSE,  FALSE,
5899     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
5900   },
5901   {
5902     Yballoon_nB,                        FALSE,  TRUE,
5903     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
5904   },
5905   {
5906     Yballoon_e,                         FALSE,  FALSE,
5907     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
5908   },
5909   {
5910     Yballoon_eB,                        FALSE,  TRUE,
5911     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
5912   },
5913   {
5914     Yballoon_s,                         FALSE,  FALSE,
5915     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
5916   },
5917   {
5918     Yballoon_sB,                        FALSE,  TRUE,
5919     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
5920   },
5921   {
5922     Yballoon_w,                         FALSE,  FALSE,
5923     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
5924   },
5925   {
5926     Yballoon_wB,                        FALSE,  TRUE,
5927     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
5928   },
5929   {
5930     Xgrass,                             TRUE,   FALSE,
5931     EL_EMC_GRASS,                       -1, -1
5932   },
5933   {
5934     Ygrass_nB,                          FALSE,  FALSE,
5935     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_UP
5936   },
5937   {
5938     Ygrass_eB,                          FALSE,  FALSE,
5939     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_RIGHT
5940   },
5941   {
5942     Ygrass_sB,                          FALSE,  FALSE,
5943     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_DOWN
5944   },
5945   {
5946     Ygrass_wB,                          FALSE,  FALSE,
5947     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_LEFT
5948   },
5949   {
5950     Xdirt,                              TRUE,   FALSE,
5951     EL_SAND,                            -1, -1
5952   },
5953   {
5954     Ydirt_nB,                           FALSE,  FALSE,
5955     EL_SAND,                            ACTION_DIGGING, MV_BIT_UP
5956   },
5957   {
5958     Ydirt_eB,                           FALSE,  FALSE,
5959     EL_SAND,                            ACTION_DIGGING, MV_BIT_RIGHT
5960   },
5961   {
5962     Ydirt_sB,                           FALSE,  FALSE,
5963     EL_SAND,                            ACTION_DIGGING, MV_BIT_DOWN
5964   },
5965   {
5966     Ydirt_wB,                           FALSE,  FALSE,
5967     EL_SAND,                            ACTION_DIGGING, MV_BIT_LEFT
5968   },
5969   {
5970     Xacid_ne,                           TRUE,   FALSE,
5971     EL_ACID_POOL_TOPRIGHT,              -1, -1
5972   },
5973   {
5974     Xacid_se,                           TRUE,   FALSE,
5975     EL_ACID_POOL_BOTTOMRIGHT,           -1, -1
5976   },
5977   {
5978     Xacid_s,                            TRUE,   FALSE,
5979     EL_ACID_POOL_BOTTOM,                -1, -1
5980   },
5981   {
5982     Xacid_sw,                           TRUE,   FALSE,
5983     EL_ACID_POOL_BOTTOMLEFT,            -1, -1
5984   },
5985   {
5986     Xacid_nw,                           TRUE,   FALSE,
5987     EL_ACID_POOL_TOPLEFT,               -1, -1
5988   },
5989   {
5990     Xacid_1,                            TRUE,   FALSE,
5991     EL_ACID,                            -1, -1
5992   },
5993   {
5994     Xacid_2,                            FALSE,  FALSE,
5995     EL_ACID,                            -1, -1
5996   },
5997   {
5998     Xacid_3,                            FALSE,  FALSE,
5999     EL_ACID,                            -1, -1
6000   },
6001   {
6002     Xacid_4,                            FALSE,  FALSE,
6003     EL_ACID,                            -1, -1
6004   },
6005   {
6006     Xacid_5,                            FALSE,  FALSE,
6007     EL_ACID,                            -1, -1
6008   },
6009   {
6010     Xacid_6,                            FALSE,  FALSE,
6011     EL_ACID,                            -1, -1
6012   },
6013   {
6014     Xacid_7,                            FALSE,  FALSE,
6015     EL_ACID,                            -1, -1
6016   },
6017   {
6018     Xacid_8,                            FALSE,  FALSE,
6019     EL_ACID,                            -1, -1
6020   },
6021   {
6022     Xball_1,                            TRUE,   FALSE,
6023     EL_EMC_MAGIC_BALL,                  -1, -1
6024   },
6025   {
6026     Xball_1B,                           FALSE,  FALSE,
6027     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
6028   },
6029   {
6030     Xball_2,                            FALSE,  FALSE,
6031     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
6032   },
6033   {
6034     Xball_2B,                           FALSE,  FALSE,
6035     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
6036   },
6037   {
6038     Yball_eat,                          FALSE,  FALSE,
6039     EL_EMC_MAGIC_BALL,                  ACTION_DROPPING, -1
6040   },
6041   {
6042     Ykey_1_eat,                         FALSE,  FALSE,
6043     EL_EM_KEY_1,                        ACTION_COLLECTING, -1
6044   },
6045   {
6046     Ykey_2_eat,                         FALSE,  FALSE,
6047     EL_EM_KEY_2,                        ACTION_COLLECTING, -1
6048   },
6049   {
6050     Ykey_3_eat,                         FALSE,  FALSE,
6051     EL_EM_KEY_3,                        ACTION_COLLECTING, -1
6052   },
6053   {
6054     Ykey_4_eat,                         FALSE,  FALSE,
6055     EL_EM_KEY_4,                        ACTION_COLLECTING, -1
6056   },
6057   {
6058     Ykey_5_eat,                         FALSE,  FALSE,
6059     EL_EMC_KEY_5,                       ACTION_COLLECTING, -1
6060   },
6061   {
6062     Ykey_6_eat,                         FALSE,  FALSE,
6063     EL_EMC_KEY_6,                       ACTION_COLLECTING, -1
6064   },
6065   {
6066     Ykey_7_eat,                         FALSE,  FALSE,
6067     EL_EMC_KEY_7,                       ACTION_COLLECTING, -1
6068   },
6069   {
6070     Ykey_8_eat,                         FALSE,  FALSE,
6071     EL_EMC_KEY_8,                       ACTION_COLLECTING, -1
6072   },
6073   {
6074     Ylenses_eat,                        FALSE,  FALSE,
6075     EL_EMC_LENSES,                      ACTION_COLLECTING, -1
6076   },
6077   {
6078     Ymagnify_eat,                       FALSE,  FALSE,
6079     EL_EMC_MAGNIFIER,                   ACTION_COLLECTING, -1
6080   },
6081   {
6082     Ygrass_eat,                         FALSE,  FALSE,
6083     EL_EMC_GRASS,                       ACTION_SNAPPING, -1
6084   },
6085   {
6086     Ydirt_eat,                          FALSE,  FALSE,
6087     EL_SAND,                            ACTION_SNAPPING, -1
6088   },
6089   {
6090     Xgrow_ns,                           TRUE,   FALSE,
6091     EL_EXPANDABLE_WALL_VERTICAL,        -1, -1
6092   },
6093   {
6094     Ygrow_ns_eat,                       FALSE,  FALSE,
6095     EL_EXPANDABLE_WALL_VERTICAL,        ACTION_GROWING, -1
6096   },
6097   {
6098     Xgrow_ew,                           TRUE,   FALSE,
6099     EL_EXPANDABLE_WALL_HORIZONTAL,      -1, -1
6100   },
6101   {
6102     Ygrow_ew_eat,                       FALSE,  FALSE,
6103     EL_EXPANDABLE_WALL_HORIZONTAL,      ACTION_GROWING, -1
6104   },
6105   {
6106     Xwonderwall,                        TRUE,   FALSE,
6107     EL_MAGIC_WALL,                      -1, -1
6108   },
6109   {
6110     XwonderwallB,                       FALSE,  FALSE,
6111     EL_MAGIC_WALL,                      ACTION_ACTIVE, -1
6112   },
6113   {
6114     Xamoeba_1,                          TRUE,   FALSE,
6115     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6116   },
6117   {
6118     Xamoeba_2,                          FALSE,  FALSE,
6119     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6120   },
6121   {
6122     Xamoeba_3,                          FALSE,  FALSE,
6123     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6124   },
6125   {
6126     Xamoeba_4,                          FALSE,  FALSE,
6127     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6128   },
6129   {
6130     Xamoeba_5,                          TRUE,   FALSE,
6131     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6132   },
6133   {
6134     Xamoeba_6,                          FALSE,  FALSE,
6135     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6136   },
6137   {
6138     Xamoeba_7,                          FALSE,  FALSE,
6139     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6140   },
6141   {
6142     Xamoeba_8,                          FALSE,  FALSE,
6143     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6144   },
6145   {
6146     Xdoor_1,                            TRUE,   FALSE,
6147     EL_EM_GATE_1,                       -1, -1
6148   },
6149   {
6150     Xdoor_2,                            TRUE,   FALSE,
6151     EL_EM_GATE_2,                       -1, -1
6152   },
6153   {
6154     Xdoor_3,                            TRUE,   FALSE,
6155     EL_EM_GATE_3,                       -1, -1
6156   },
6157   {
6158     Xdoor_4,                            TRUE,   FALSE,
6159     EL_EM_GATE_4,                       -1, -1
6160   },
6161   {
6162     Xdoor_5,                            TRUE,   FALSE,
6163     EL_EMC_GATE_5,                      -1, -1
6164   },
6165   {
6166     Xdoor_6,                            TRUE,   FALSE,
6167     EL_EMC_GATE_6,                      -1, -1
6168   },
6169   {
6170     Xdoor_7,                            TRUE,   FALSE,
6171     EL_EMC_GATE_7,                      -1, -1
6172   },
6173   {
6174     Xdoor_8,                            TRUE,   FALSE,
6175     EL_EMC_GATE_8,                      -1, -1
6176   },
6177   {
6178     Xkey_1,                             TRUE,   FALSE,
6179     EL_EM_KEY_1,                        -1, -1
6180   },
6181   {
6182     Xkey_2,                             TRUE,   FALSE,
6183     EL_EM_KEY_2,                        -1, -1
6184   },
6185   {
6186     Xkey_3,                             TRUE,   FALSE,
6187     EL_EM_KEY_3,                        -1, -1
6188   },
6189   {
6190     Xkey_4,                             TRUE,   FALSE,
6191     EL_EM_KEY_4,                        -1, -1
6192   },
6193   {
6194     Xkey_5,                             TRUE,   FALSE,
6195     EL_EMC_KEY_5,                       -1, -1
6196   },
6197   {
6198     Xkey_6,                             TRUE,   FALSE,
6199     EL_EMC_KEY_6,                       -1, -1
6200   },
6201   {
6202     Xkey_7,                             TRUE,   FALSE,
6203     EL_EMC_KEY_7,                       -1, -1
6204   },
6205   {
6206     Xkey_8,                             TRUE,   FALSE,
6207     EL_EMC_KEY_8,                       -1, -1
6208   },
6209   {
6210     Xwind_n,                            TRUE,   FALSE,
6211     EL_BALLOON_SWITCH_UP,               -1, -1
6212   },
6213   {
6214     Xwind_e,                            TRUE,   FALSE,
6215     EL_BALLOON_SWITCH_RIGHT,            -1, -1
6216   },
6217   {
6218     Xwind_s,                            TRUE,   FALSE,
6219     EL_BALLOON_SWITCH_DOWN,             -1, -1
6220   },
6221   {
6222     Xwind_w,                            TRUE,   FALSE,
6223     EL_BALLOON_SWITCH_LEFT,             -1, -1
6224   },
6225   {
6226     Xwind_nesw,                         TRUE,   FALSE,
6227     EL_BALLOON_SWITCH_ANY,              -1, -1
6228   },
6229   {
6230     Xwind_stop,                         TRUE,   FALSE,
6231     EL_BALLOON_SWITCH_NONE,             -1, -1
6232   },
6233   {
6234     Xexit,                              TRUE,   FALSE,
6235     EL_EM_EXIT_CLOSED,                  -1, -1
6236   },
6237   {
6238     Xexit_1,                            TRUE,   FALSE,
6239     EL_EM_EXIT_OPEN,                    -1, -1
6240   },
6241   {
6242     Xexit_2,                            FALSE,  FALSE,
6243     EL_EM_EXIT_OPEN,                    -1, -1
6244   },
6245   {
6246     Xexit_3,                            FALSE,  FALSE,
6247     EL_EM_EXIT_OPEN,                    -1, -1
6248   },
6249   {
6250     Xdynamite,                          TRUE,   FALSE,
6251     EL_EM_DYNAMITE,                     -1, -1
6252   },
6253   {
6254     Ydynamite_eat,                      FALSE,  FALSE,
6255     EL_EM_DYNAMITE,                     ACTION_COLLECTING, -1
6256   },
6257   {
6258     Xdynamite_1,                        TRUE,   FALSE,
6259     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6260   },
6261   {
6262     Xdynamite_2,                        FALSE,  FALSE,
6263     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6264   },
6265   {
6266     Xdynamite_3,                        FALSE,  FALSE,
6267     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6268   },
6269   {
6270     Xdynamite_4,                        FALSE,  FALSE,
6271     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6272   },
6273   {
6274     Xbumper,                            TRUE,   FALSE,
6275     EL_EMC_SPRING_BUMPER,               -1, -1
6276   },
6277   {
6278     XbumperB,                           FALSE,  FALSE,
6279     EL_EMC_SPRING_BUMPER,               ACTION_ACTIVE, -1
6280   },
6281   {
6282     Xwheel,                             TRUE,   FALSE,
6283     EL_ROBOT_WHEEL,                     -1, -1
6284   },
6285   {
6286     XwheelB,                            FALSE,  FALSE,
6287     EL_ROBOT_WHEEL,                     ACTION_ACTIVE, -1
6288   },
6289   {
6290     Xswitch,                            TRUE,   FALSE,
6291     EL_EMC_MAGIC_BALL_SWITCH,           -1, -1
6292   },
6293   {
6294     XswitchB,                           FALSE,  FALSE,
6295     EL_EMC_MAGIC_BALL_SWITCH,           ACTION_ACTIVE, -1
6296   },
6297   {
6298     Xsand,                              TRUE,   FALSE,
6299     EL_QUICKSAND_EMPTY,                 -1, -1
6300   },
6301   {
6302     Xsand_stone,                        TRUE,   FALSE,
6303     EL_QUICKSAND_FULL,                  -1, -1
6304   },
6305   {
6306     Xsand_stonein_1,                    FALSE,  TRUE,
6307     EL_ROCK,                            ACTION_FILLING, -1
6308   },
6309   {
6310     Xsand_stonein_2,                    FALSE,  TRUE,
6311     EL_ROCK,                            ACTION_FILLING, -1
6312   },
6313   {
6314     Xsand_stonein_3,                    FALSE,  TRUE,
6315     EL_ROCK,                            ACTION_FILLING, -1
6316   },
6317   {
6318     Xsand_stonein_4,                    FALSE,  TRUE,
6319     EL_ROCK,                            ACTION_FILLING, -1
6320   },
6321   {
6322     Xsand_stonesand_1,                  FALSE,  FALSE,
6323     EL_QUICKSAND_EMPTYING,              -1, -1
6324   },
6325   {
6326     Xsand_stonesand_2,                  FALSE,  FALSE,
6327     EL_QUICKSAND_EMPTYING,              -1, -1
6328   },
6329   {
6330     Xsand_stonesand_3,                  FALSE,  FALSE,
6331     EL_QUICKSAND_EMPTYING,              -1, -1
6332   },
6333   {
6334     Xsand_stonesand_4,                  FALSE,  FALSE,
6335     EL_QUICKSAND_EMPTYING,              -1, -1
6336   },
6337   {
6338     Xsand_stonesand_quickout_1,         FALSE,  FALSE,
6339     EL_QUICKSAND_EMPTYING,              -1, -1
6340   },
6341   {
6342     Xsand_stonesand_quickout_2,         FALSE,  FALSE,
6343     EL_QUICKSAND_EMPTYING,              -1, -1
6344   },
6345   {
6346     Xsand_stoneout_1,                   FALSE,  FALSE,
6347     EL_ROCK,                            ACTION_EMPTYING, -1
6348   },
6349   {
6350     Xsand_stoneout_2,                   FALSE,  FALSE,
6351     EL_ROCK,                            ACTION_EMPTYING, -1
6352   },
6353   {
6354     Xsand_sandstone_1,                  FALSE,  FALSE,
6355     EL_QUICKSAND_FILLING,               -1, -1
6356   },
6357   {
6358     Xsand_sandstone_2,                  FALSE,  FALSE,
6359     EL_QUICKSAND_FILLING,               -1, -1
6360   },
6361   {
6362     Xsand_sandstone_3,                  FALSE,  FALSE,
6363     EL_QUICKSAND_FILLING,               -1, -1
6364   },
6365   {
6366     Xsand_sandstone_4,                  FALSE,  FALSE,
6367     EL_QUICKSAND_FILLING,               -1, -1
6368   },
6369   {
6370     Xplant,                             TRUE,   FALSE,
6371     EL_EMC_PLANT,                       -1, -1
6372   },
6373   {
6374     Yplant,                             FALSE,  FALSE,
6375     EL_EMC_PLANT,                       -1, -1
6376   },
6377   {
6378     Xlenses,                            TRUE,   FALSE,
6379     EL_EMC_LENSES,                      -1, -1
6380   },
6381   {
6382     Xmagnify,                           TRUE,   FALSE,
6383     EL_EMC_MAGNIFIER,                   -1, -1
6384   },
6385   {
6386     Xdripper,                           TRUE,   FALSE,
6387     EL_EMC_DRIPPER,                     -1, -1
6388   },
6389   {
6390     XdripperB,                          FALSE,  FALSE,
6391     EL_EMC_DRIPPER,                     ACTION_ACTIVE, -1
6392   },
6393   {
6394     Xfake_blank,                        TRUE,   FALSE,
6395     EL_INVISIBLE_WALL,                  -1, -1
6396   },
6397   {
6398     Xfake_blankB,                       FALSE,  FALSE,
6399     EL_INVISIBLE_WALL,                  ACTION_ACTIVE, -1
6400   },
6401   {
6402     Xfake_grass,                        TRUE,   FALSE,
6403     EL_EMC_FAKE_GRASS,                  -1, -1
6404   },
6405   {
6406     Xfake_grassB,                       FALSE,  FALSE,
6407     EL_EMC_FAKE_GRASS,                  ACTION_ACTIVE, -1
6408   },
6409   {
6410     Xfake_door_1,                       TRUE,   FALSE,
6411     EL_EM_GATE_1_GRAY,                  -1, -1
6412   },
6413   {
6414     Xfake_door_2,                       TRUE,   FALSE,
6415     EL_EM_GATE_2_GRAY,                  -1, -1
6416   },
6417   {
6418     Xfake_door_3,                       TRUE,   FALSE,
6419     EL_EM_GATE_3_GRAY,                  -1, -1
6420   },
6421   {
6422     Xfake_door_4,                       TRUE,   FALSE,
6423     EL_EM_GATE_4_GRAY,                  -1, -1
6424   },
6425   {
6426     Xfake_door_5,                       TRUE,   FALSE,
6427     EL_EMC_GATE_5_GRAY,                 -1, -1
6428   },
6429   {
6430     Xfake_door_6,                       TRUE,   FALSE,
6431     EL_EMC_GATE_6_GRAY,                 -1, -1
6432   },
6433   {
6434     Xfake_door_7,                       TRUE,   FALSE,
6435     EL_EMC_GATE_7_GRAY,                 -1, -1
6436   },
6437   {
6438     Xfake_door_8,                       TRUE,   FALSE,
6439     EL_EMC_GATE_8_GRAY,                 -1, -1
6440   },
6441   {
6442     Xfake_acid_1,                       TRUE,   FALSE,
6443     EL_EMC_FAKE_ACID,                   -1, -1
6444   },
6445   {
6446     Xfake_acid_2,                       FALSE,  FALSE,
6447     EL_EMC_FAKE_ACID,                   -1, -1
6448   },
6449   {
6450     Xfake_acid_3,                       FALSE,  FALSE,
6451     EL_EMC_FAKE_ACID,                   -1, -1
6452   },
6453   {
6454     Xfake_acid_4,                       FALSE,  FALSE,
6455     EL_EMC_FAKE_ACID,                   -1, -1
6456   },
6457   {
6458     Xfake_acid_5,                       FALSE,  FALSE,
6459     EL_EMC_FAKE_ACID,                   -1, -1
6460   },
6461   {
6462     Xfake_acid_6,                       FALSE,  FALSE,
6463     EL_EMC_FAKE_ACID,                   -1, -1
6464   },
6465   {
6466     Xfake_acid_7,                       FALSE,  FALSE,
6467     EL_EMC_FAKE_ACID,                   -1, -1
6468   },
6469   {
6470     Xfake_acid_8,                       FALSE,  FALSE,
6471     EL_EMC_FAKE_ACID,                   -1, -1
6472   },
6473   {
6474     Xsteel_1,                           TRUE,   FALSE,
6475     EL_STEELWALL,                       -1, -1
6476   },
6477   {
6478     Xsteel_2,                           TRUE,   FALSE,
6479     EL_EMC_STEELWALL_2,                 -1, -1
6480   },
6481   {
6482     Xsteel_3,                           TRUE,   FALSE,
6483     EL_EMC_STEELWALL_3,                 -1, -1
6484   },
6485   {
6486     Xsteel_4,                           TRUE,   FALSE,
6487     EL_EMC_STEELWALL_4,                 -1, -1
6488   },
6489   {
6490     Xwall_1,                            TRUE,   FALSE,
6491     EL_WALL,                            -1, -1
6492   },
6493   {
6494     Xwall_2,                            TRUE,   FALSE,
6495     EL_EMC_WALL_14,                     -1, -1
6496   },
6497   {
6498     Xwall_3,                            TRUE,   FALSE,
6499     EL_EMC_WALL_15,                     -1, -1
6500   },
6501   {
6502     Xwall_4,                            TRUE,   FALSE,
6503     EL_EMC_WALL_16,                     -1, -1
6504   },
6505   {
6506     Xround_wall_1,                      TRUE,   FALSE,
6507     EL_WALL_SLIPPERY,                   -1, -1
6508   },
6509   {
6510     Xround_wall_2,                      TRUE,   FALSE,
6511     EL_EMC_WALL_SLIPPERY_2,             -1, -1
6512   },
6513   {
6514     Xround_wall_3,                      TRUE,   FALSE,
6515     EL_EMC_WALL_SLIPPERY_3,             -1, -1
6516   },
6517   {
6518     Xround_wall_4,                      TRUE,   FALSE,
6519     EL_EMC_WALL_SLIPPERY_4,             -1, -1
6520   },
6521   {
6522     Xdecor_1,                           TRUE,   FALSE,
6523     EL_EMC_WALL_8,                      -1, -1
6524   },
6525   {
6526     Xdecor_2,                           TRUE,   FALSE,
6527     EL_EMC_WALL_6,                      -1, -1
6528   },
6529   {
6530     Xdecor_3,                           TRUE,   FALSE,
6531     EL_EMC_WALL_4,                      -1, -1
6532   },
6533   {
6534     Xdecor_4,                           TRUE,   FALSE,
6535     EL_EMC_WALL_7,                      -1, -1
6536   },
6537   {
6538     Xdecor_5,                           TRUE,   FALSE,
6539     EL_EMC_WALL_5,                      -1, -1
6540   },
6541   {
6542     Xdecor_6,                           TRUE,   FALSE,
6543     EL_EMC_WALL_9,                      -1, -1
6544   },
6545   {
6546     Xdecor_7,                           TRUE,   FALSE,
6547     EL_EMC_WALL_10,                     -1, -1
6548   },
6549   {
6550     Xdecor_8,                           TRUE,   FALSE,
6551     EL_EMC_WALL_1,                      -1, -1
6552   },
6553   {
6554     Xdecor_9,                           TRUE,   FALSE,
6555     EL_EMC_WALL_2,                      -1, -1
6556   },
6557   {
6558     Xdecor_10,                          TRUE,   FALSE,
6559     EL_EMC_WALL_3,                      -1, -1
6560   },
6561   {
6562     Xdecor_11,                          TRUE,   FALSE,
6563     EL_EMC_WALL_11,                     -1, -1
6564   },
6565   {
6566     Xdecor_12,                          TRUE,   FALSE,
6567     EL_EMC_WALL_12,                     -1, -1
6568   },
6569   {
6570     Xalpha_0,                           TRUE,   FALSE,
6571     EL_CHAR('0'),                       -1, -1
6572   },
6573   {
6574     Xalpha_1,                           TRUE,   FALSE,
6575     EL_CHAR('1'),                       -1, -1
6576   },
6577   {
6578     Xalpha_2,                           TRUE,   FALSE,
6579     EL_CHAR('2'),                       -1, -1
6580   },
6581   {
6582     Xalpha_3,                           TRUE,   FALSE,
6583     EL_CHAR('3'),                       -1, -1
6584   },
6585   {
6586     Xalpha_4,                           TRUE,   FALSE,
6587     EL_CHAR('4'),                       -1, -1
6588   },
6589   {
6590     Xalpha_5,                           TRUE,   FALSE,
6591     EL_CHAR('5'),                       -1, -1
6592   },
6593   {
6594     Xalpha_6,                           TRUE,   FALSE,
6595     EL_CHAR('6'),                       -1, -1
6596   },
6597   {
6598     Xalpha_7,                           TRUE,   FALSE,
6599     EL_CHAR('7'),                       -1, -1
6600   },
6601   {
6602     Xalpha_8,                           TRUE,   FALSE,
6603     EL_CHAR('8'),                       -1, -1
6604   },
6605   {
6606     Xalpha_9,                           TRUE,   FALSE,
6607     EL_CHAR('9'),                       -1, -1
6608   },
6609   {
6610     Xalpha_excla,                       TRUE,   FALSE,
6611     EL_CHAR('!'),                       -1, -1
6612   },
6613   {
6614     Xalpha_quote,                       TRUE,   FALSE,
6615     EL_CHAR('"'),                       -1, -1
6616   },
6617   {
6618     Xalpha_comma,                       TRUE,   FALSE,
6619     EL_CHAR(','),                       -1, -1
6620   },
6621   {
6622     Xalpha_minus,                       TRUE,   FALSE,
6623     EL_CHAR('-'),                       -1, -1
6624   },
6625   {
6626     Xalpha_perio,                       TRUE,   FALSE,
6627     EL_CHAR('.'),                       -1, -1
6628   },
6629   {
6630     Xalpha_colon,                       TRUE,   FALSE,
6631     EL_CHAR(':'),                       -1, -1
6632   },
6633   {
6634     Xalpha_quest,                       TRUE,   FALSE,
6635     EL_CHAR('?'),                       -1, -1
6636   },
6637   {
6638     Xalpha_a,                           TRUE,   FALSE,
6639     EL_CHAR('A'),                       -1, -1
6640   },
6641   {
6642     Xalpha_b,                           TRUE,   FALSE,
6643     EL_CHAR('B'),                       -1, -1
6644   },
6645   {
6646     Xalpha_c,                           TRUE,   FALSE,
6647     EL_CHAR('C'),                       -1, -1
6648   },
6649   {
6650     Xalpha_d,                           TRUE,   FALSE,
6651     EL_CHAR('D'),                       -1, -1
6652   },
6653   {
6654     Xalpha_e,                           TRUE,   FALSE,
6655     EL_CHAR('E'),                       -1, -1
6656   },
6657   {
6658     Xalpha_f,                           TRUE,   FALSE,
6659     EL_CHAR('F'),                       -1, -1
6660   },
6661   {
6662     Xalpha_g,                           TRUE,   FALSE,
6663     EL_CHAR('G'),                       -1, -1
6664   },
6665   {
6666     Xalpha_h,                           TRUE,   FALSE,
6667     EL_CHAR('H'),                       -1, -1
6668   },
6669   {
6670     Xalpha_i,                           TRUE,   FALSE,
6671     EL_CHAR('I'),                       -1, -1
6672   },
6673   {
6674     Xalpha_j,                           TRUE,   FALSE,
6675     EL_CHAR('J'),                       -1, -1
6676   },
6677   {
6678     Xalpha_k,                           TRUE,   FALSE,
6679     EL_CHAR('K'),                       -1, -1
6680   },
6681   {
6682     Xalpha_l,                           TRUE,   FALSE,
6683     EL_CHAR('L'),                       -1, -1
6684   },
6685   {
6686     Xalpha_m,                           TRUE,   FALSE,
6687     EL_CHAR('M'),                       -1, -1
6688   },
6689   {
6690     Xalpha_n,                           TRUE,   FALSE,
6691     EL_CHAR('N'),                       -1, -1
6692   },
6693   {
6694     Xalpha_o,                           TRUE,   FALSE,
6695     EL_CHAR('O'),                       -1, -1
6696   },
6697   {
6698     Xalpha_p,                           TRUE,   FALSE,
6699     EL_CHAR('P'),                       -1, -1
6700   },
6701   {
6702     Xalpha_q,                           TRUE,   FALSE,
6703     EL_CHAR('Q'),                       -1, -1
6704   },
6705   {
6706     Xalpha_r,                           TRUE,   FALSE,
6707     EL_CHAR('R'),                       -1, -1
6708   },
6709   {
6710     Xalpha_s,                           TRUE,   FALSE,
6711     EL_CHAR('S'),                       -1, -1
6712   },
6713   {
6714     Xalpha_t,                           TRUE,   FALSE,
6715     EL_CHAR('T'),                       -1, -1
6716   },
6717   {
6718     Xalpha_u,                           TRUE,   FALSE,
6719     EL_CHAR('U'),                       -1, -1
6720   },
6721   {
6722     Xalpha_v,                           TRUE,   FALSE,
6723     EL_CHAR('V'),                       -1, -1
6724   },
6725   {
6726     Xalpha_w,                           TRUE,   FALSE,
6727     EL_CHAR('W'),                       -1, -1
6728   },
6729   {
6730     Xalpha_x,                           TRUE,   FALSE,
6731     EL_CHAR('X'),                       -1, -1
6732   },
6733   {
6734     Xalpha_y,                           TRUE,   FALSE,
6735     EL_CHAR('Y'),                       -1, -1
6736   },
6737   {
6738     Xalpha_z,                           TRUE,   FALSE,
6739     EL_CHAR('Z'),                       -1, -1
6740   },
6741   {
6742     Xalpha_arrow_e,                     TRUE,   FALSE,
6743     EL_CHAR('>'),                       -1, -1
6744   },
6745   {
6746     Xalpha_arrow_w,                     TRUE,   FALSE,
6747     EL_CHAR('<'),                       -1, -1
6748   },
6749   {
6750     Xalpha_copyr,                       TRUE,   FALSE,
6751     EL_CHAR(CHAR_BYTE_COPYRIGHT),       -1, -1
6752   },
6753
6754   {
6755     Xboom_bug,                          FALSE,  FALSE,
6756     EL_BUG,                             ACTION_EXPLODING, -1
6757   },
6758   {
6759     Xboom_bomb,                         FALSE,  FALSE,
6760     EL_BOMB,                            ACTION_EXPLODING, -1
6761   },
6762   {
6763     Xboom_android,                      FALSE,  FALSE,
6764     EL_EMC_ANDROID,                     ACTION_OTHER, -1
6765   },
6766   {
6767     Xboom_1,                            FALSE,  FALSE,
6768     EL_DEFAULT,                         ACTION_EXPLODING, -1
6769   },
6770   {
6771     Xboom_2,                            FALSE,  FALSE,
6772     EL_DEFAULT,                         ACTION_EXPLODING, -1
6773   },
6774   {
6775     Znormal,                            FALSE,  FALSE,
6776     EL_EMPTY,                           -1, -1
6777   },
6778   {
6779     Zdynamite,                          FALSE,  FALSE,
6780     EL_EMPTY,                           -1, -1
6781   },
6782   {
6783     Zplayer,                            FALSE,  FALSE,
6784     EL_EMPTY,                           -1, -1
6785   },
6786   {
6787     ZBORDER,                            FALSE,  FALSE,
6788     EL_EMPTY,                           -1, -1
6789   },
6790
6791   {
6792     -1,                                 FALSE,  FALSE,
6793     -1,                                 -1, -1
6794   }
6795 };
6796
6797 static struct Mapping_EM_to_RND_player
6798 {
6799   int action_em;
6800   int player_nr;
6801
6802   int element_rnd;
6803   int action;
6804   int direction;
6805 }
6806 em_player_mapping_list[] =
6807 {
6808   {
6809     SPR_walk + 0,                       0,
6810     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_UP,
6811   },
6812   {
6813     SPR_walk + 1,                       0,
6814     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_RIGHT,
6815   },
6816   {
6817     SPR_walk + 2,                       0,
6818     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_DOWN,
6819   },
6820   {
6821     SPR_walk + 3,                       0,
6822     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_LEFT,
6823   },
6824   {
6825     SPR_push + 0,                       0,
6826     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_UP,
6827   },
6828   {
6829     SPR_push + 1,                       0,
6830     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_RIGHT,
6831   },
6832   {
6833     SPR_push + 2,                       0,
6834     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_DOWN,
6835   },
6836   {
6837     SPR_push + 3,                       0,
6838     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_LEFT,
6839   },
6840   {
6841     SPR_spray + 0,                      0,
6842     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_UP,
6843   },
6844   {
6845     SPR_spray + 1,                      0,
6846     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_RIGHT,
6847   },
6848   {
6849     SPR_spray + 2,                      0,
6850     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_DOWN,
6851   },
6852   {
6853     SPR_spray + 3,                      0,
6854     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_LEFT,
6855   },
6856   {
6857     SPR_walk + 0,                       1,
6858     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_UP,
6859   },
6860   {
6861     SPR_walk + 1,                       1,
6862     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_RIGHT,
6863   },
6864   {
6865     SPR_walk + 2,                       1,
6866     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_DOWN,
6867   },
6868   {
6869     SPR_walk + 3,                       1,
6870     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_LEFT,
6871   },
6872   {
6873     SPR_push + 0,                       1,
6874     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_UP,
6875   },
6876   {
6877     SPR_push + 1,                       1,
6878     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_RIGHT,
6879   },
6880   {
6881     SPR_push + 2,                       1,
6882     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_DOWN,
6883   },
6884   {
6885     SPR_push + 3,                       1,
6886     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_LEFT,
6887   },
6888   {
6889     SPR_spray + 0,                      1,
6890     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_UP,
6891   },
6892   {
6893     SPR_spray + 1,                      1,
6894     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_RIGHT,
6895   },
6896   {
6897     SPR_spray + 2,                      1,
6898     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_DOWN,
6899   },
6900   {
6901     SPR_spray + 3,                      1,
6902     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_LEFT,
6903   },
6904   {
6905     SPR_still,                          0,
6906     EL_PLAYER_1,                        ACTION_DEFAULT, -1,
6907   },
6908   {
6909     SPR_still,                          1,
6910     EL_PLAYER_2,                        ACTION_DEFAULT, -1,
6911   },
6912   {
6913     SPR_walk + 0,                       2,
6914     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_UP,
6915   },
6916   {
6917     SPR_walk + 1,                       2,
6918     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_RIGHT,
6919   },
6920   {
6921     SPR_walk + 2,                       2,
6922     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_DOWN,
6923   },
6924   {
6925     SPR_walk + 3,                       2,
6926     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_LEFT,
6927   },
6928   {
6929     SPR_push + 0,                       2,
6930     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_UP,
6931   },
6932   {
6933     SPR_push + 1,                       2,
6934     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_RIGHT,
6935   },
6936   {
6937     SPR_push + 2,                       2,
6938     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_DOWN,
6939   },
6940   {
6941     SPR_push + 3,                       2,
6942     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_LEFT,
6943   },
6944   {
6945     SPR_spray + 0,                      2,
6946     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_UP,
6947   },
6948   {
6949     SPR_spray + 1,                      2,
6950     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_RIGHT,
6951   },
6952   {
6953     SPR_spray + 2,                      2,
6954     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_DOWN,
6955   },
6956   {
6957     SPR_spray + 3,                      2,
6958     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_LEFT,
6959   },
6960   {
6961     SPR_walk + 0,                       3,
6962     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_UP,
6963   },
6964   {
6965     SPR_walk + 1,                       3,
6966     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_RIGHT,
6967   },
6968   {
6969     SPR_walk + 2,                       3,
6970     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_DOWN,
6971   },
6972   {
6973     SPR_walk + 3,                       3,
6974     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_LEFT,
6975   },
6976   {
6977     SPR_push + 0,                       3,
6978     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_UP,
6979   },
6980   {
6981     SPR_push + 1,                       3,
6982     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_RIGHT,
6983   },
6984   {
6985     SPR_push + 2,                       3,
6986     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_DOWN,
6987   },
6988   {
6989     SPR_push + 3,                       3,
6990     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_LEFT,
6991   },
6992   {
6993     SPR_spray + 0,                      3,
6994     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_UP,
6995   },
6996   {
6997     SPR_spray + 1,                      3,
6998     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_RIGHT,
6999   },
7000   {
7001     SPR_spray + 2,                      3,
7002     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_DOWN,
7003   },
7004   {
7005     SPR_spray + 3,                      3,
7006     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_LEFT,
7007   },
7008   {
7009     SPR_still,                          2,
7010     EL_PLAYER_3,                        ACTION_DEFAULT, -1,
7011   },
7012   {
7013     SPR_still,                          3,
7014     EL_PLAYER_4,                        ACTION_DEFAULT, -1,
7015   },
7016
7017   {
7018     -1,                                 -1,
7019     -1,                                 -1, -1
7020   }
7021 };
7022
7023 int map_element_RND_to_EM(int element_rnd)
7024 {
7025   static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7026   static boolean mapping_initialized = FALSE;
7027
7028   if (!mapping_initialized)
7029   {
7030     int i;
7031
7032     /* return "Xalpha_quest" for all undefined elements in mapping array */
7033     for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7034       mapping_RND_to_EM[i] = Xalpha_quest;
7035
7036     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7037       if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7038         mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7039           em_object_mapping_list[i].element_em;
7040
7041     mapping_initialized = TRUE;
7042   }
7043
7044   if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7045     return mapping_RND_to_EM[element_rnd];
7046
7047   Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7048
7049   return EL_UNKNOWN;
7050 }
7051
7052 int map_element_EM_to_RND(int element_em)
7053 {
7054   static unsigned short mapping_EM_to_RND[TILE_MAX];
7055   static boolean mapping_initialized = FALSE;
7056
7057   if (!mapping_initialized)
7058   {
7059     int i;
7060
7061     /* return "EL_UNKNOWN" for all undefined elements in mapping array */
7062     for (i = 0; i < TILE_MAX; i++)
7063       mapping_EM_to_RND[i] = EL_UNKNOWN;
7064
7065     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7066       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7067         em_object_mapping_list[i].element_rnd;
7068
7069     mapping_initialized = TRUE;
7070   }
7071
7072   if (element_em >= 0 && element_em < TILE_MAX)
7073     return mapping_EM_to_RND[element_em];
7074
7075   Error(ERR_WARN, "invalid EM level element %d", element_em);
7076
7077   return EL_UNKNOWN;
7078 }
7079
7080 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7081 {
7082   struct LevelInfo_EM *level_em = level->native_em_level;
7083   struct LEVEL *lev = level_em->lev;
7084   int i, j;
7085
7086   for (i = 0; i < TILE_MAX; i++)
7087     lev->android_array[i] = Xblank;
7088
7089   for (i = 0; i < level->num_android_clone_elements; i++)
7090   {
7091     int element_rnd = level->android_clone_element[i];
7092     int element_em = map_element_RND_to_EM(element_rnd);
7093
7094     for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7095       if (em_object_mapping_list[j].element_rnd == element_rnd)
7096         lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7097   }
7098 }
7099
7100 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7101 {
7102   struct LevelInfo_EM *level_em = level->native_em_level;
7103   struct LEVEL *lev = level_em->lev;
7104   int i, j;
7105
7106   level->num_android_clone_elements = 0;
7107
7108   for (i = 0; i < TILE_MAX; i++)
7109   {
7110     int element_em = lev->android_array[i];
7111     int element_rnd;
7112     boolean element_found = FALSE;
7113
7114     if (element_em == Xblank)
7115       continue;
7116
7117     element_rnd = map_element_EM_to_RND(element_em);
7118
7119     for (j = 0; j < level->num_android_clone_elements; j++)
7120       if (level->android_clone_element[j] == element_rnd)
7121         element_found = TRUE;
7122
7123     if (!element_found)
7124     {
7125       level->android_clone_element[level->num_android_clone_elements++] =
7126         element_rnd;
7127
7128       if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7129         break;
7130     }
7131   }
7132
7133   if (level->num_android_clone_elements == 0)
7134   {
7135     level->num_android_clone_elements = 1;
7136     level->android_clone_element[0] = EL_EMPTY;
7137   }
7138 }
7139
7140 int map_direction_RND_to_EM(int direction)
7141 {
7142   return (direction == MV_UP    ? 0 :
7143           direction == MV_RIGHT ? 1 :
7144           direction == MV_DOWN  ? 2 :
7145           direction == MV_LEFT  ? 3 :
7146           -1);
7147 }
7148
7149 int map_direction_EM_to_RND(int direction)
7150 {
7151   return (direction == 0 ? MV_UP    :
7152           direction == 1 ? MV_RIGHT :
7153           direction == 2 ? MV_DOWN  :
7154           direction == 3 ? MV_LEFT  :
7155           MV_NONE);
7156 }
7157
7158 int map_element_RND_to_SP(int element_rnd)
7159 {
7160   int element_sp = 0x20;        /* map unknown elements to yellow "hardware" */
7161
7162   if (element_rnd >= EL_SP_START &&
7163       element_rnd <= EL_SP_END)
7164     element_sp = element_rnd - EL_SP_START;
7165   else if (element_rnd == EL_EMPTY_SPACE)
7166     element_sp = 0x00;
7167   else if (element_rnd == EL_INVISIBLE_WALL)
7168     element_sp = 0x28;
7169
7170   return element_sp;
7171 }
7172
7173 int map_element_SP_to_RND(int element_sp)
7174 {
7175   int element_rnd = EL_UNKNOWN;
7176
7177   if (element_sp >= 0x00 &&
7178       element_sp <= 0x27)
7179     element_rnd = EL_SP_START + element_sp;
7180   else if (element_sp == 0x28)
7181     element_rnd = EL_INVISIBLE_WALL;
7182
7183   return element_rnd;
7184 }
7185
7186 int map_action_SP_to_RND(int action_sp)
7187 {
7188   switch (action_sp)
7189   {
7190     case actActive:             return ACTION_ACTIVE;
7191     case actImpact:             return ACTION_IMPACT;
7192     case actExploding:          return ACTION_EXPLODING;
7193     case actDigging:            return ACTION_DIGGING;
7194     case actSnapping:           return ACTION_SNAPPING;
7195     case actCollecting:         return ACTION_COLLECTING;
7196     case actPassing:            return ACTION_PASSING;
7197     case actPushing:            return ACTION_PUSHING;
7198     case actDropping:           return ACTION_DROPPING;
7199
7200     default:                    return ACTION_DEFAULT;
7201   }
7202 }
7203
7204 int get_next_element(int element)
7205 {
7206   switch (element)
7207   {
7208     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
7209     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
7210     case EL_QUICKSAND_FAST_FILLING:     return EL_QUICKSAND_FAST_FULL;
7211     case EL_QUICKSAND_FAST_EMPTYING:    return EL_QUICKSAND_FAST_EMPTY;
7212     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
7213     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
7214     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
7215     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
7216     case EL_DC_MAGIC_WALL_FILLING:      return EL_DC_MAGIC_WALL_FULL;
7217     case EL_DC_MAGIC_WALL_EMPTYING:     return EL_DC_MAGIC_WALL_ACTIVE;
7218     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
7219
7220     default:                            return element;
7221   }
7222 }
7223
7224 int el_act_dir2img(int element, int action, int direction)
7225 {
7226   element = GFX_ELEMENT(element);
7227   direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7228
7229   /* direction_graphic[][] == graphic[] for undefined direction graphics */
7230   return element_info[element].direction_graphic[action][direction];
7231 }
7232
7233 static int el_act_dir2crm(int element, int action, int direction)
7234 {
7235   element = GFX_ELEMENT(element);
7236   direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7237
7238   /* direction_graphic[][] == graphic[] for undefined direction graphics */
7239   return element_info[element].direction_crumbled[action][direction];
7240 }
7241
7242 int el_act2img(int element, int action)
7243 {
7244   element = GFX_ELEMENT(element);
7245
7246   return element_info[element].graphic[action];
7247 }
7248
7249 int el_act2crm(int element, int action)
7250 {
7251   element = GFX_ELEMENT(element);
7252
7253   return element_info[element].crumbled[action];
7254 }
7255
7256 int el_dir2img(int element, int direction)
7257 {
7258   element = GFX_ELEMENT(element);
7259
7260   return el_act_dir2img(element, ACTION_DEFAULT, direction);
7261 }
7262
7263 int el2baseimg(int element)
7264 {
7265   return element_info[element].graphic[ACTION_DEFAULT];
7266 }
7267
7268 int el2img(int element)
7269 {
7270   element = GFX_ELEMENT(element);
7271
7272   return element_info[element].graphic[ACTION_DEFAULT];
7273 }
7274
7275 int el2edimg(int element)
7276 {
7277   element = GFX_ELEMENT(element);
7278
7279   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7280 }
7281
7282 int el2preimg(int element)
7283 {
7284   element = GFX_ELEMENT(element);
7285
7286   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7287 }
7288
7289 int el2panelimg(int element)
7290 {
7291   element = GFX_ELEMENT(element);
7292
7293   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7294 }
7295
7296 int font2baseimg(int font_nr)
7297 {
7298   return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7299 }
7300
7301 int getBeltNrFromBeltElement(int element)
7302 {
7303   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7304           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7305           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7306 }
7307
7308 int getBeltNrFromBeltActiveElement(int element)
7309 {
7310   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7311           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7312           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7313 }
7314
7315 int getBeltNrFromBeltSwitchElement(int element)
7316 {
7317   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
7318           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
7319           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
7320 }
7321
7322 int getBeltDirNrFromBeltElement(int element)
7323 {
7324   static int belt_base_element[4] =
7325   {
7326     EL_CONVEYOR_BELT_1_LEFT,
7327     EL_CONVEYOR_BELT_2_LEFT,
7328     EL_CONVEYOR_BELT_3_LEFT,
7329     EL_CONVEYOR_BELT_4_LEFT
7330   };
7331
7332   int belt_nr = getBeltNrFromBeltElement(element);
7333   int belt_dir_nr = element - belt_base_element[belt_nr];
7334
7335   return (belt_dir_nr % 3);
7336 }
7337
7338 int getBeltDirNrFromBeltSwitchElement(int element)
7339 {
7340   static int belt_base_element[4] =
7341   {
7342     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7343     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7344     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7345     EL_CONVEYOR_BELT_4_SWITCH_LEFT
7346   };
7347
7348   int belt_nr = getBeltNrFromBeltSwitchElement(element);
7349   int belt_dir_nr = element - belt_base_element[belt_nr];
7350
7351   return (belt_dir_nr % 3);
7352 }
7353
7354 int getBeltDirFromBeltElement(int element)
7355 {
7356   static int belt_move_dir[3] =
7357   {
7358     MV_LEFT,
7359     MV_NONE,
7360     MV_RIGHT
7361   };
7362
7363   int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7364
7365   return belt_move_dir[belt_dir_nr];
7366 }
7367
7368 int getBeltDirFromBeltSwitchElement(int element)
7369 {
7370   static int belt_move_dir[3] =
7371   {
7372     MV_LEFT,
7373     MV_NONE,
7374     MV_RIGHT
7375   };
7376
7377   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7378
7379   return belt_move_dir[belt_dir_nr];
7380 }
7381
7382 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7383 {
7384   static int belt_base_element[4] =
7385   {
7386     EL_CONVEYOR_BELT_1_LEFT,
7387     EL_CONVEYOR_BELT_2_LEFT,
7388     EL_CONVEYOR_BELT_3_LEFT,
7389     EL_CONVEYOR_BELT_4_LEFT
7390   };
7391
7392   return belt_base_element[belt_nr] + belt_dir_nr;
7393 }
7394
7395 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7396 {
7397   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7398
7399   return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7400 }
7401
7402 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7403 {
7404   static int belt_base_element[4] =
7405   {
7406     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7407     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7408     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7409     EL_CONVEYOR_BELT_4_SWITCH_LEFT
7410   };
7411
7412   return belt_base_element[belt_nr] + belt_dir_nr;
7413 }
7414
7415 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7416 {
7417   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7418
7419   return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7420 }
7421
7422 boolean getTeamMode_EM()
7423 {
7424   return game.team_mode;
7425 }
7426
7427 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7428 {
7429   int game_frame_delay_value;
7430
7431   game_frame_delay_value =
7432     (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7433      GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7434      GameFrameDelay);
7435
7436   if (tape.playing && tape.warp_forward && !tape.pausing)
7437     game_frame_delay_value = 0;
7438
7439   return game_frame_delay_value;
7440 }
7441
7442 unsigned int InitRND(int seed)
7443 {
7444   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7445     return InitEngineRandom_EM(seed);
7446   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7447     return InitEngineRandom_SP(seed);
7448   else
7449     return InitEngineRandom_RND(seed);
7450 }
7451
7452 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7453 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7454
7455 inline static int get_effective_element_EM(int tile, int frame_em)
7456 {
7457   int element             = object_mapping[tile].element_rnd;
7458   int action              = object_mapping[tile].action;
7459   boolean is_backside     = object_mapping[tile].is_backside;
7460   boolean action_removing = (action == ACTION_DIGGING ||
7461                              action == ACTION_SNAPPING ||
7462                              action == ACTION_COLLECTING);
7463
7464   if (frame_em < 7)
7465   {
7466     switch (tile)
7467     {
7468       case Yacid_splash_eB:
7469       case Yacid_splash_wB:
7470         return (frame_em > 5 ? EL_EMPTY : element);
7471
7472       default:
7473         return element;
7474     }
7475   }
7476   else  /* frame_em == 7 */
7477   {
7478     switch (tile)
7479     {
7480       case Yacid_splash_eB:
7481       case Yacid_splash_wB:
7482         return EL_EMPTY;
7483
7484       case Yemerald_stone:
7485         return EL_EMERALD;
7486
7487       case Ydiamond_stone:
7488         return EL_ROCK;
7489
7490       case Xdrip_stretch:
7491       case Xdrip_stretchB:
7492       case Ydrip_s1:
7493       case Ydrip_s1B:
7494       case Xball_1B:
7495       case Xball_2:
7496       case Xball_2B:
7497       case Yball_eat:
7498       case Ykey_1_eat:
7499       case Ykey_2_eat:
7500       case Ykey_3_eat:
7501       case Ykey_4_eat:
7502       case Ykey_5_eat:
7503       case Ykey_6_eat:
7504       case Ykey_7_eat:
7505       case Ykey_8_eat:
7506       case Ylenses_eat:
7507       case Ymagnify_eat:
7508       case Ygrass_eat:
7509       case Ydirt_eat:
7510       case Xsand_stonein_1:
7511       case Xsand_stonein_2:
7512       case Xsand_stonein_3:
7513       case Xsand_stonein_4:
7514         return element;
7515
7516       default:
7517         return (is_backside || action_removing ? EL_EMPTY : element);
7518     }
7519   }
7520 }
7521
7522 inline static boolean check_linear_animation_EM(int tile)
7523 {
7524   switch (tile)
7525   {
7526     case Xsand_stonesand_1:
7527     case Xsand_stonesand_quickout_1:
7528     case Xsand_sandstone_1:
7529     case Xsand_stonein_1:
7530     case Xsand_stoneout_1:
7531     case Xboom_1:
7532     case Xdynamite_1:
7533     case Ybug_w_n:
7534     case Ybug_n_e:
7535     case Ybug_e_s:
7536     case Ybug_s_w:
7537     case Ybug_e_n:
7538     case Ybug_s_e:
7539     case Ybug_w_s:
7540     case Ybug_n_w:
7541     case Ytank_w_n:
7542     case Ytank_n_e:
7543     case Ytank_e_s:
7544     case Ytank_s_w:
7545     case Ytank_e_n:
7546     case Ytank_s_e:
7547     case Ytank_w_s:
7548     case Ytank_n_w:
7549     case Yacid_splash_eB:
7550     case Yacid_splash_wB:
7551     case Yemerald_stone:
7552       return TRUE;
7553   }
7554
7555   return FALSE;
7556 }
7557
7558 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7559                                             boolean has_crumbled_graphics,
7560                                             int crumbled, int sync_frame)
7561 {
7562   /* if element can be crumbled, but certain action graphics are just empty
7563      space (like instantly snapping sand to empty space in 1 frame), do not
7564      treat these empty space graphics as crumbled graphics in EMC engine */
7565   if (crumbled == IMG_EMPTY_SPACE)
7566     has_crumbled_graphics = FALSE;
7567
7568   if (has_crumbled_graphics)
7569   {
7570     struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7571     int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7572                                            g_crumbled->anim_delay,
7573                                            g_crumbled->anim_mode,
7574                                            g_crumbled->anim_start_frame,
7575                                            sync_frame);
7576
7577     getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7578                      &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7579
7580     g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7581     g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
7582
7583     g_em->has_crumbled_graphics = TRUE;
7584   }
7585   else
7586   {
7587     g_em->crumbled_bitmap = NULL;
7588     g_em->crumbled_src_x = 0;
7589     g_em->crumbled_src_y = 0;
7590     g_em->crumbled_border_size = 0;
7591     g_em->crumbled_tile_size = 0;
7592
7593     g_em->has_crumbled_graphics = FALSE;
7594   }
7595 }
7596
7597 void ResetGfxAnimation_EM(int x, int y, int tile)
7598 {
7599   GfxFrame[x][y] = 0;
7600 }
7601
7602 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7603                         int tile, int frame_em, int x, int y)
7604 {
7605   int action = object_mapping[tile].action;
7606   int direction = object_mapping[tile].direction;
7607   int effective_element = get_effective_element_EM(tile, frame_em);
7608   int graphic = (direction == MV_NONE ?
7609                  el_act2img(effective_element, action) :
7610                  el_act_dir2img(effective_element, action, direction));
7611   struct GraphicInfo *g = &graphic_info[graphic];
7612   int sync_frame;
7613   boolean action_removing = (action == ACTION_DIGGING ||
7614                              action == ACTION_SNAPPING ||
7615                              action == ACTION_COLLECTING);
7616   boolean action_moving   = (action == ACTION_FALLING ||
7617                              action == ACTION_MOVING ||
7618                              action == ACTION_PUSHING ||
7619                              action == ACTION_EATING ||
7620                              action == ACTION_FILLING ||
7621                              action == ACTION_EMPTYING);
7622   boolean action_falling  = (action == ACTION_FALLING ||
7623                              action == ACTION_FILLING ||
7624                              action == ACTION_EMPTYING);
7625
7626   /* special case: graphic uses "2nd movement tile" and has defined
7627      7 frames for movement animation (or less) => use default graphic
7628      for last (8th) frame which ends the movement animation */
7629   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7630   {
7631     action = ACTION_DEFAULT;    /* (keep action_* unchanged for now) */
7632     graphic = (direction == MV_NONE ?
7633                el_act2img(effective_element, action) :
7634                el_act_dir2img(effective_element, action, direction));
7635
7636     g = &graphic_info[graphic];
7637   }
7638
7639   if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7640   {
7641     GfxFrame[x][y] = 0;
7642   }
7643   else if (action_moving)
7644   {
7645     boolean is_backside = object_mapping[tile].is_backside;
7646
7647     if (is_backside)
7648     {
7649       int direction = object_mapping[tile].direction;
7650       int move_dir = (action_falling ? MV_DOWN : direction);
7651
7652       GfxFrame[x][y]++;
7653
7654 #if 1
7655       /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7656       if (g->double_movement && frame_em == 0)
7657         GfxFrame[x][y] = 0;
7658 #endif
7659
7660       if (move_dir == MV_LEFT)
7661         GfxFrame[x - 1][y] = GfxFrame[x][y];
7662       else if (move_dir == MV_RIGHT)
7663         GfxFrame[x + 1][y] = GfxFrame[x][y];
7664       else if (move_dir == MV_UP)
7665         GfxFrame[x][y - 1] = GfxFrame[x][y];
7666       else if (move_dir == MV_DOWN)
7667         GfxFrame[x][y + 1] = GfxFrame[x][y];
7668     }
7669   }
7670   else
7671   {
7672     GfxFrame[x][y]++;
7673
7674     /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7675     if (tile == Xsand_stonesand_quickout_1 ||
7676         tile == Xsand_stonesand_quickout_2)
7677       GfxFrame[x][y]++;
7678   }
7679
7680   if (graphic_info[graphic].anim_global_sync)
7681     sync_frame = FrameCounter;
7682   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7683     sync_frame = GfxFrame[x][y];
7684   else
7685     sync_frame = 0;     /* playfield border (pseudo steel) */
7686
7687   SetRandomAnimationValue(x, y);
7688
7689   int frame = getAnimationFrame(g->anim_frames,
7690                                 g->anim_delay,
7691                                 g->anim_mode,
7692                                 g->anim_start_frame,
7693                                 sync_frame);
7694
7695   g_em->unique_identifier =
7696     (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7697 }
7698
7699 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7700                                   int tile, int frame_em, int x, int y)
7701 {
7702   int action = object_mapping[tile].action;
7703   int direction = object_mapping[tile].direction;
7704   boolean is_backside = object_mapping[tile].is_backside;
7705   int effective_element = get_effective_element_EM(tile, frame_em);
7706   int effective_action = action;
7707   int graphic = (direction == MV_NONE ?
7708                  el_act2img(effective_element, effective_action) :
7709                  el_act_dir2img(effective_element, effective_action,
7710                                 direction));
7711   int crumbled = (direction == MV_NONE ?
7712                   el_act2crm(effective_element, effective_action) :
7713                   el_act_dir2crm(effective_element, effective_action,
7714                                  direction));
7715   int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7716   int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7717   boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7718   struct GraphicInfo *g = &graphic_info[graphic];
7719   int sync_frame;
7720
7721   /* special case: graphic uses "2nd movement tile" and has defined
7722      7 frames for movement animation (or less) => use default graphic
7723      for last (8th) frame which ends the movement animation */
7724   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7725   {
7726     effective_action = ACTION_DEFAULT;
7727     graphic = (direction == MV_NONE ?
7728                el_act2img(effective_element, effective_action) :
7729                el_act_dir2img(effective_element, effective_action,
7730                               direction));
7731     crumbled = (direction == MV_NONE ?
7732                 el_act2crm(effective_element, effective_action) :
7733                 el_act_dir2crm(effective_element, effective_action,
7734                                direction));
7735
7736     g = &graphic_info[graphic];
7737   }
7738
7739   if (graphic_info[graphic].anim_global_sync)
7740     sync_frame = FrameCounter;
7741   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7742     sync_frame = GfxFrame[x][y];
7743   else
7744     sync_frame = 0;     /* playfield border (pseudo steel) */
7745
7746   SetRandomAnimationValue(x, y);
7747
7748   int frame = getAnimationFrame(g->anim_frames,
7749                                 g->anim_delay,
7750                                 g->anim_mode,
7751                                 g->anim_start_frame,
7752                                 sync_frame);
7753
7754   getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7755                       g->double_movement && is_backside);
7756
7757   /* (updating the "crumbled" graphic definitions is probably not really needed,
7758      as animations for crumbled graphics can't be longer than one EMC cycle) */
7759   set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7760                            sync_frame);
7761 }
7762
7763 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7764                                   int player_nr, int anim, int frame_em)
7765 {
7766   int element   = player_mapping[player_nr][anim].element_rnd;
7767   int action    = player_mapping[player_nr][anim].action;
7768   int direction = player_mapping[player_nr][anim].direction;
7769   int graphic = (direction == MV_NONE ?
7770                  el_act2img(element, action) :
7771                  el_act_dir2img(element, action, direction));
7772   struct GraphicInfo *g = &graphic_info[graphic];
7773   int sync_frame;
7774
7775   InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7776
7777   stored_player[player_nr].StepFrame = frame_em;
7778
7779   sync_frame = stored_player[player_nr].Frame;
7780
7781   int frame = getAnimationFrame(g->anim_frames,
7782                                 g->anim_delay,
7783                                 g->anim_mode,
7784                                 g->anim_start_frame,
7785                                 sync_frame);
7786
7787   getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7788                       &g_em->src_x, &g_em->src_y, FALSE);
7789 }
7790
7791 void InitGraphicInfo_EM(void)
7792 {
7793   int i, j, p;
7794
7795 #if DEBUG_EM_GFX
7796   int num_em_gfx_errors = 0;
7797
7798   if (graphic_info_em_object[0][0].bitmap == NULL)
7799   {
7800     /* EM graphics not yet initialized in em_open_all() */
7801
7802     return;
7803   }
7804
7805   printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7806 #endif
7807
7808   /* always start with reliable default values */
7809   for (i = 0; i < TILE_MAX; i++)
7810   {
7811     object_mapping[i].element_rnd = EL_UNKNOWN;
7812     object_mapping[i].is_backside = FALSE;
7813     object_mapping[i].action = ACTION_DEFAULT;
7814     object_mapping[i].direction = MV_NONE;
7815   }
7816
7817   /* always start with reliable default values */
7818   for (p = 0; p < MAX_PLAYERS; p++)
7819   {
7820     for (i = 0; i < SPR_MAX; i++)
7821     {
7822       player_mapping[p][i].element_rnd = EL_UNKNOWN;
7823       player_mapping[p][i].action = ACTION_DEFAULT;
7824       player_mapping[p][i].direction = MV_NONE;
7825     }
7826   }
7827
7828   for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7829   {
7830     int e = em_object_mapping_list[i].element_em;
7831
7832     object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7833     object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7834
7835     if (em_object_mapping_list[i].action != -1)
7836       object_mapping[e].action = em_object_mapping_list[i].action;
7837
7838     if (em_object_mapping_list[i].direction != -1)
7839       object_mapping[e].direction =
7840         MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7841   }
7842
7843   for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7844   {
7845     int a = em_player_mapping_list[i].action_em;
7846     int p = em_player_mapping_list[i].player_nr;
7847
7848     player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7849
7850     if (em_player_mapping_list[i].action != -1)
7851       player_mapping[p][a].action = em_player_mapping_list[i].action;
7852
7853     if (em_player_mapping_list[i].direction != -1)
7854       player_mapping[p][a].direction =
7855         MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7856   }
7857
7858   for (i = 0; i < TILE_MAX; i++)
7859   {
7860     int element = object_mapping[i].element_rnd;
7861     int action = object_mapping[i].action;
7862     int direction = object_mapping[i].direction;
7863     boolean is_backside = object_mapping[i].is_backside;
7864     boolean action_exploding = ((action == ACTION_EXPLODING ||
7865                                  action == ACTION_SMASHED_BY_ROCK ||
7866                                  action == ACTION_SMASHED_BY_SPRING) &&
7867                                 element != EL_DIAMOND);
7868     boolean action_active = (action == ACTION_ACTIVE);
7869     boolean action_other = (action == ACTION_OTHER);
7870
7871     for (j = 0; j < 8; j++)
7872     {
7873       int effective_element = get_effective_element_EM(i, j);
7874       int effective_action = (j < 7 ? action :
7875                               i == Xdrip_stretch ? action :
7876                               i == Xdrip_stretchB ? action :
7877                               i == Ydrip_s1 ? action :
7878                               i == Ydrip_s1B ? action :
7879                               i == Xball_1B ? action :
7880                               i == Xball_2 ? action :
7881                               i == Xball_2B ? action :
7882                               i == Yball_eat ? action :
7883                               i == Ykey_1_eat ? action :
7884                               i == Ykey_2_eat ? action :
7885                               i == Ykey_3_eat ? action :
7886                               i == Ykey_4_eat ? action :
7887                               i == Ykey_5_eat ? action :
7888                               i == Ykey_6_eat ? action :
7889                               i == Ykey_7_eat ? action :
7890                               i == Ykey_8_eat ? action :
7891                               i == Ylenses_eat ? action :
7892                               i == Ymagnify_eat ? action :
7893                               i == Ygrass_eat ? action :
7894                               i == Ydirt_eat ? action :
7895                               i == Xsand_stonein_1 ? action :
7896                               i == Xsand_stonein_2 ? action :
7897                               i == Xsand_stonein_3 ? action :
7898                               i == Xsand_stonein_4 ? action :
7899                               i == Xsand_stoneout_1 ? action :
7900                               i == Xsand_stoneout_2 ? action :
7901                               i == Xboom_android ? ACTION_EXPLODING :
7902                               action_exploding ? ACTION_EXPLODING :
7903                               action_active ? action :
7904                               action_other ? action :
7905                               ACTION_DEFAULT);
7906       int graphic = (el_act_dir2img(effective_element, effective_action,
7907                                     direction));
7908       int crumbled = (el_act_dir2crm(effective_element, effective_action,
7909                                      direction));
7910       int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7911       int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7912       boolean has_action_graphics = (graphic != base_graphic);
7913       boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7914       struct GraphicInfo *g = &graphic_info[graphic];
7915       struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7916       Bitmap *src_bitmap;
7917       int src_x, src_y;
7918       /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7919       boolean special_animation = (action != ACTION_DEFAULT &&
7920                                    g->anim_frames == 3 &&
7921                                    g->anim_delay == 2 &&
7922                                    g->anim_mode & ANIM_LINEAR);
7923       int sync_frame = (i == Xdrip_stretch ? 7 :
7924                         i == Xdrip_stretchB ? 7 :
7925                         i == Ydrip_s2 ? j + 8 :
7926                         i == Ydrip_s2B ? j + 8 :
7927                         i == Xacid_1 ? 0 :
7928                         i == Xacid_2 ? 10 :
7929                         i == Xacid_3 ? 20 :
7930                         i == Xacid_4 ? 30 :
7931                         i == Xacid_5 ? 40 :
7932                         i == Xacid_6 ? 50 :
7933                         i == Xacid_7 ? 60 :
7934                         i == Xacid_8 ? 70 :
7935                         i == Xfake_acid_1 ? 0 :
7936                         i == Xfake_acid_2 ? 10 :
7937                         i == Xfake_acid_3 ? 20 :
7938                         i == Xfake_acid_4 ? 30 :
7939                         i == Xfake_acid_5 ? 40 :
7940                         i == Xfake_acid_6 ? 50 :
7941                         i == Xfake_acid_7 ? 60 :
7942                         i == Xfake_acid_8 ? 70 :
7943                         i == Xball_2 ? 7 :
7944                         i == Xball_2B ? j + 8 :
7945                         i == Yball_eat ? j + 1 :
7946                         i == Ykey_1_eat ? j + 1 :
7947                         i == Ykey_2_eat ? j + 1 :
7948                         i == Ykey_3_eat ? j + 1 :
7949                         i == Ykey_4_eat ? j + 1 :
7950                         i == Ykey_5_eat ? j + 1 :
7951                         i == Ykey_6_eat ? j + 1 :
7952                         i == Ykey_7_eat ? j + 1 :
7953                         i == Ykey_8_eat ? j + 1 :
7954                         i == Ylenses_eat ? j + 1 :
7955                         i == Ymagnify_eat ? j + 1 :
7956                         i == Ygrass_eat ? j + 1 :
7957                         i == Ydirt_eat ? j + 1 :
7958                         i == Xamoeba_1 ? 0 :
7959                         i == Xamoeba_2 ? 1 :
7960                         i == Xamoeba_3 ? 2 :
7961                         i == Xamoeba_4 ? 3 :
7962                         i == Xamoeba_5 ? 0 :
7963                         i == Xamoeba_6 ? 1 :
7964                         i == Xamoeba_7 ? 2 :
7965                         i == Xamoeba_8 ? 3 :
7966                         i == Xexit_2 ? j + 8 :
7967                         i == Xexit_3 ? j + 16 :
7968                         i == Xdynamite_1 ? 0 :
7969                         i == Xdynamite_2 ? 8 :
7970                         i == Xdynamite_3 ? 16 :
7971                         i == Xdynamite_4 ? 24 :
7972                         i == Xsand_stonein_1 ? j + 1 :
7973                         i == Xsand_stonein_2 ? j + 9 :
7974                         i == Xsand_stonein_3 ? j + 17 :
7975                         i == Xsand_stonein_4 ? j + 25 :
7976                         i == Xsand_stoneout_1 && j == 0 ? 0 :
7977                         i == Xsand_stoneout_1 && j == 1 ? 0 :
7978                         i == Xsand_stoneout_1 && j == 2 ? 1 :
7979                         i == Xsand_stoneout_1 && j == 3 ? 2 :
7980                         i == Xsand_stoneout_1 && j == 4 ? 2 :
7981                         i == Xsand_stoneout_1 && j == 5 ? 3 :
7982                         i == Xsand_stoneout_1 && j == 6 ? 4 :
7983                         i == Xsand_stoneout_1 && j == 7 ? 4 :
7984                         i == Xsand_stoneout_2 && j == 0 ? 5 :
7985                         i == Xsand_stoneout_2 && j == 1 ? 6 :
7986                         i == Xsand_stoneout_2 && j == 2 ? 7 :
7987                         i == Xsand_stoneout_2 && j == 3 ? 8 :
7988                         i == Xsand_stoneout_2 && j == 4 ? 9 :
7989                         i == Xsand_stoneout_2 && j == 5 ? 11 :
7990                         i == Xsand_stoneout_2 && j == 6 ? 13 :
7991                         i == Xsand_stoneout_2 && j == 7 ? 15 :
7992                         i == Xboom_bug && j == 1 ? 2 :
7993                         i == Xboom_bug && j == 2 ? 2 :
7994                         i == Xboom_bug && j == 3 ? 4 :
7995                         i == Xboom_bug && j == 4 ? 4 :
7996                         i == Xboom_bug && j == 5 ? 2 :
7997                         i == Xboom_bug && j == 6 ? 2 :
7998                         i == Xboom_bug && j == 7 ? 0 :
7999                         i == Xboom_bomb && j == 1 ? 2 :
8000                         i == Xboom_bomb && j == 2 ? 2 :
8001                         i == Xboom_bomb && j == 3 ? 4 :
8002                         i == Xboom_bomb && j == 4 ? 4 :
8003                         i == Xboom_bomb && j == 5 ? 2 :
8004                         i == Xboom_bomb && j == 6 ? 2 :
8005                         i == Xboom_bomb && j == 7 ? 0 :
8006                         i == Xboom_android && j == 7 ? 6 :
8007                         i == Xboom_1 && j == 1 ? 2 :
8008                         i == Xboom_1 && j == 2 ? 2 :
8009                         i == Xboom_1 && j == 3 ? 4 :
8010                         i == Xboom_1 && j == 4 ? 4 :
8011                         i == Xboom_1 && j == 5 ? 6 :
8012                         i == Xboom_1 && j == 6 ? 6 :
8013                         i == Xboom_1 && j == 7 ? 8 :
8014                         i == Xboom_2 && j == 0 ? 8 :
8015                         i == Xboom_2 && j == 1 ? 8 :
8016                         i == Xboom_2 && j == 2 ? 10 :
8017                         i == Xboom_2 && j == 3 ? 10 :
8018                         i == Xboom_2 && j == 4 ? 10 :
8019                         i == Xboom_2 && j == 5 ? 12 :
8020                         i == Xboom_2 && j == 6 ? 12 :
8021                         i == Xboom_2 && j == 7 ? 12 :
8022                         special_animation && j == 4 ? 3 :
8023                         effective_action != action ? 0 :
8024                         j);
8025
8026 #if DEBUG_EM_GFX
8027       Bitmap *debug_bitmap = g_em->bitmap;
8028       int debug_src_x = g_em->src_x;
8029       int debug_src_y = g_em->src_y;
8030 #endif
8031
8032       int frame = getAnimationFrame(g->anim_frames,
8033                                     g->anim_delay,
8034                                     g->anim_mode,
8035                                     g->anim_start_frame,
8036                                     sync_frame);
8037
8038       getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8039                           g->double_movement && is_backside);
8040
8041       g_em->bitmap = src_bitmap;
8042       g_em->src_x = src_x;
8043       g_em->src_y = src_y;
8044       g_em->src_offset_x = 0;
8045       g_em->src_offset_y = 0;
8046       g_em->dst_offset_x = 0;
8047       g_em->dst_offset_y = 0;
8048       g_em->width  = TILEX;
8049       g_em->height = TILEY;
8050
8051       g_em->preserve_background = FALSE;
8052
8053       set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8054                                sync_frame);
8055
8056       if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8057                                    effective_action == ACTION_MOVING  ||
8058                                    effective_action == ACTION_PUSHING ||
8059                                    effective_action == ACTION_EATING)) ||
8060           (!has_action_graphics && (effective_action == ACTION_FILLING ||
8061                                     effective_action == ACTION_EMPTYING)))
8062       {
8063         int move_dir =
8064           (effective_action == ACTION_FALLING ||
8065            effective_action == ACTION_FILLING ||
8066            effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8067         int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8068         int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? 1 : 0);
8069         int num_steps = (i == Ydrip_s1  ? 16 :
8070                          i == Ydrip_s1B ? 16 :
8071                          i == Ydrip_s2  ? 16 :
8072                          i == Ydrip_s2B ? 16 :
8073                          i == Xsand_stonein_1 ? 32 :
8074                          i == Xsand_stonein_2 ? 32 :
8075                          i == Xsand_stonein_3 ? 32 :
8076                          i == Xsand_stonein_4 ? 32 :
8077                          i == Xsand_stoneout_1 ? 16 :
8078                          i == Xsand_stoneout_2 ? 16 : 8);
8079         int cx = ABS(dx) * (TILEX / num_steps);
8080         int cy = ABS(dy) * (TILEY / num_steps);
8081         int step_frame = (i == Ydrip_s2         ? j + 8 :
8082                           i == Ydrip_s2B        ? j + 8 :
8083                           i == Xsand_stonein_2  ? j + 8 :
8084                           i == Xsand_stonein_3  ? j + 16 :
8085                           i == Xsand_stonein_4  ? j + 24 :
8086                           i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8087         int step = (is_backside ? step_frame : num_steps - step_frame);
8088
8089         if (is_backside)        /* tile where movement starts */
8090         {
8091           if (dx < 0 || dy < 0)
8092           {
8093             g_em->src_offset_x = cx * step;
8094             g_em->src_offset_y = cy * step;
8095           }
8096           else
8097           {
8098             g_em->dst_offset_x = cx * step;
8099             g_em->dst_offset_y = cy * step;
8100           }
8101         }
8102         else                    /* tile where movement ends */
8103         {
8104           if (dx < 0 || dy < 0)
8105           {
8106             g_em->dst_offset_x = cx * step;
8107             g_em->dst_offset_y = cy * step;
8108           }
8109           else
8110           {
8111             g_em->src_offset_x = cx * step;
8112             g_em->src_offset_y = cy * step;
8113           }
8114         }
8115
8116         g_em->width  = TILEX - cx * step;
8117         g_em->height = TILEY - cy * step;
8118       }
8119
8120       /* create unique graphic identifier to decide if tile must be redrawn */
8121       /* bit 31 - 16 (16 bit): EM style graphic
8122          bit 15 - 12 ( 4 bit): EM style frame
8123          bit 11 -  6 ( 6 bit): graphic width
8124          bit  5 -  0 ( 6 bit): graphic height */
8125       g_em->unique_identifier =
8126         (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8127
8128 #if DEBUG_EM_GFX
8129
8130       /* skip check for EMC elements not contained in original EMC artwork */
8131       if (element == EL_EMC_FAKE_ACID)
8132         continue;
8133
8134       if (g_em->bitmap != debug_bitmap ||
8135           g_em->src_x != debug_src_x ||
8136           g_em->src_y != debug_src_y ||
8137           g_em->src_offset_x != 0 ||
8138           g_em->src_offset_y != 0 ||
8139           g_em->dst_offset_x != 0 ||
8140           g_em->dst_offset_y != 0 ||
8141           g_em->width != TILEX ||
8142           g_em->height != TILEY)
8143       {
8144         static int last_i = -1;
8145
8146         if (i != last_i)
8147         {
8148           printf("\n");
8149           last_i = i;
8150         }
8151
8152         printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8153                i, element, element_info[element].token_name,
8154                element_action_info[effective_action].suffix, direction);
8155
8156         if (element != effective_element)
8157           printf(" [%d ('%s')]",
8158                  effective_element,
8159                  element_info[effective_element].token_name);
8160
8161         printf("\n");
8162
8163         if (g_em->bitmap != debug_bitmap)
8164           printf("    %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8165                  j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8166
8167         if (g_em->src_x != debug_src_x ||
8168             g_em->src_y != debug_src_y)
8169           printf("    frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8170                  j, (is_backside ? 'B' : 'F'),
8171                  g_em->src_x, g_em->src_y,
8172                  g_em->src_x / 32, g_em->src_y / 32,
8173                  debug_src_x, debug_src_y,
8174                  debug_src_x / 32, debug_src_y / 32);
8175
8176         if (g_em->src_offset_x != 0 ||
8177             g_em->src_offset_y != 0 ||
8178             g_em->dst_offset_x != 0 ||
8179             g_em->dst_offset_y != 0)
8180           printf("    %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8181                  j, is_backside,
8182                  g_em->src_offset_x, g_em->src_offset_y,
8183                  g_em->dst_offset_x, g_em->dst_offset_y);
8184
8185         if (g_em->width != TILEX ||
8186             g_em->height != TILEY)
8187           printf("    %d (%d): size %d,%d should be %d,%d\n",
8188                  j, is_backside,
8189                  g_em->width, g_em->height, TILEX, TILEY);
8190
8191         num_em_gfx_errors++;
8192       }
8193 #endif
8194
8195     }
8196   }
8197
8198   for (i = 0; i < TILE_MAX; i++)
8199   {
8200     for (j = 0; j < 8; j++)
8201     {
8202       int element = object_mapping[i].element_rnd;
8203       int action = object_mapping[i].action;
8204       int direction = object_mapping[i].direction;
8205       boolean is_backside = object_mapping[i].is_backside;
8206       int graphic_action  = el_act_dir2img(element, action, direction);
8207       int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8208
8209       if ((action == ACTION_SMASHED_BY_ROCK ||
8210            action == ACTION_SMASHED_BY_SPRING ||
8211            action == ACTION_EATING) &&
8212           graphic_action == graphic_default)
8213       {
8214         int e = (action == ACTION_SMASHED_BY_ROCK   ? Ystone_s  :
8215                  action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8216                  direction == MV_LEFT  ? (is_backside? Yspring_wB: Yspring_w) :
8217                  direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8218                  Xspring);
8219
8220         /* no separate animation for "smashed by rock" -- use rock instead */
8221         struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8222         struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8223
8224         g_em->bitmap            = g_xx->bitmap;
8225         g_em->src_x             = g_xx->src_x;
8226         g_em->src_y             = g_xx->src_y;
8227         g_em->src_offset_x      = g_xx->src_offset_x;
8228         g_em->src_offset_y      = g_xx->src_offset_y;
8229         g_em->dst_offset_x      = g_xx->dst_offset_x;
8230         g_em->dst_offset_y      = g_xx->dst_offset_y;
8231         g_em->width             = g_xx->width;
8232         g_em->height            = g_xx->height;
8233         g_em->unique_identifier = g_xx->unique_identifier;
8234
8235         if (!is_backside)
8236           g_em->preserve_background = TRUE;
8237       }
8238     }
8239   }
8240
8241   for (p = 0; p < MAX_PLAYERS; p++)
8242   {
8243     for (i = 0; i < SPR_MAX; i++)
8244     {
8245       int element = player_mapping[p][i].element_rnd;
8246       int action = player_mapping[p][i].action;
8247       int direction = player_mapping[p][i].direction;
8248
8249       for (j = 0; j < 8; j++)
8250       {
8251         int effective_element = element;
8252         int effective_action = action;
8253         int graphic = (direction == MV_NONE ?
8254                        el_act2img(effective_element, effective_action) :
8255                        el_act_dir2img(effective_element, effective_action,
8256                                       direction));
8257         struct GraphicInfo *g = &graphic_info[graphic];
8258         struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8259         Bitmap *src_bitmap;
8260         int src_x, src_y;
8261         int sync_frame = j;
8262
8263 #if DEBUG_EM_GFX
8264         Bitmap *debug_bitmap = g_em->bitmap;
8265         int debug_src_x = g_em->src_x;
8266         int debug_src_y = g_em->src_y;
8267 #endif
8268
8269         int frame = getAnimationFrame(g->anim_frames,
8270                                       g->anim_delay,
8271                                       g->anim_mode,
8272                                       g->anim_start_frame,
8273                                       sync_frame);
8274
8275         getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8276
8277         g_em->bitmap = src_bitmap;
8278         g_em->src_x = src_x;
8279         g_em->src_y = src_y;
8280         g_em->src_offset_x = 0;
8281         g_em->src_offset_y = 0;
8282         g_em->dst_offset_x = 0;
8283         g_em->dst_offset_y = 0;
8284         g_em->width  = TILEX;
8285         g_em->height = TILEY;
8286
8287 #if DEBUG_EM_GFX
8288
8289         /* skip check for EMC elements not contained in original EMC artwork */
8290         if (element == EL_PLAYER_3 ||
8291             element == EL_PLAYER_4)
8292           continue;
8293
8294         if (g_em->bitmap != debug_bitmap ||
8295             g_em->src_x != debug_src_x ||
8296             g_em->src_y != debug_src_y)
8297         {
8298           static int last_i = -1;
8299
8300           if (i != last_i)
8301           {
8302             printf("\n");
8303             last_i = i;
8304           }
8305
8306           printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8307                  p, i, element, element_info[element].token_name,
8308                  element_action_info[effective_action].suffix, direction);
8309
8310           if (element != effective_element)
8311             printf(" [%d ('%s')]",
8312                    effective_element,
8313                    element_info[effective_element].token_name);
8314
8315           printf("\n");
8316
8317           if (g_em->bitmap != debug_bitmap)
8318             printf("    %d: different bitmap! (0x%08x != 0x%08x)\n",
8319                    j, (int)(g_em->bitmap), (int)(debug_bitmap));
8320
8321           if (g_em->src_x != debug_src_x ||
8322               g_em->src_y != debug_src_y)
8323             printf("    frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8324                    j,
8325                    g_em->src_x, g_em->src_y,
8326                    g_em->src_x / 32, g_em->src_y / 32,
8327                    debug_src_x, debug_src_y,
8328                    debug_src_x / 32, debug_src_y / 32);
8329
8330           num_em_gfx_errors++;
8331         }
8332 #endif
8333
8334       }
8335     }
8336   }
8337
8338 #if DEBUG_EM_GFX
8339   printf("\n");
8340   printf("::: [%d errors found]\n", num_em_gfx_errors);
8341
8342   exit(0);
8343 #endif
8344 }
8345
8346 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8347                                 boolean any_player_moving,
8348                                 boolean any_player_snapping,
8349                                 boolean any_player_dropping)
8350 {
8351   if (frame == 0 && !any_player_dropping)
8352   {
8353     if (!local_player->was_waiting)
8354     {
8355       if (!CheckSaveEngineSnapshotToList())
8356         return;
8357
8358       local_player->was_waiting = TRUE;
8359     }
8360   }
8361   else if (any_player_moving || any_player_snapping || any_player_dropping)
8362   {
8363     local_player->was_waiting = FALSE;
8364   }
8365 }
8366
8367 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8368                                 boolean murphy_is_dropping)
8369 {
8370   if (murphy_is_waiting)
8371   {
8372     if (!local_player->was_waiting)
8373     {
8374       if (!CheckSaveEngineSnapshotToList())
8375         return;
8376
8377       local_player->was_waiting = TRUE;
8378     }
8379   }
8380   else
8381   {
8382     local_player->was_waiting = FALSE;
8383   }
8384 }
8385
8386 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8387                             boolean any_player_moving,
8388                             boolean any_player_snapping,
8389                             boolean any_player_dropping)
8390 {
8391   if (tape.single_step && tape.recording && !tape.pausing)
8392     if (frame == 0 && !any_player_dropping)
8393       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8394
8395   CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8396                              any_player_snapping, any_player_dropping);
8397 }
8398
8399 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8400                             boolean murphy_is_dropping)
8401 {
8402   boolean murphy_starts_dropping = FALSE;
8403   int i;
8404
8405   for (i = 0; i < MAX_PLAYERS; i++)
8406     if (stored_player[i].force_dropping)
8407       murphy_starts_dropping = TRUE;
8408
8409   if (tape.single_step && tape.recording && !tape.pausing)
8410     if (murphy_is_waiting && !murphy_starts_dropping)
8411       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8412
8413   CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8414 }
8415
8416 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8417                          int graphic, int sync_frame, int x, int y)
8418 {
8419   int frame = getGraphicAnimationFrame(graphic, sync_frame);
8420
8421   getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8422 }
8423
8424 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8425 {
8426   return (IS_NEXT_FRAME(sync_frame, graphic));
8427 }
8428
8429 int getGraphicInfo_Delay(int graphic)
8430 {
8431   return graphic_info[graphic].anim_delay;
8432 }
8433
8434 void PlayMenuSoundExt(int sound)
8435 {
8436   if (sound == SND_UNDEFINED)
8437     return;
8438
8439   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8440       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8441     return;
8442
8443   if (IS_LOOP_SOUND(sound))
8444     PlaySoundLoop(sound);
8445   else
8446     PlaySound(sound);
8447 }
8448
8449 void PlayMenuSound()
8450 {
8451   PlayMenuSoundExt(menu.sound[game_status]);
8452 }
8453
8454 void PlayMenuSoundStereo(int sound, int stereo_position)
8455 {
8456   if (sound == SND_UNDEFINED)
8457     return;
8458
8459   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8460       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8461     return;
8462
8463   if (IS_LOOP_SOUND(sound))
8464     PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8465   else
8466     PlaySoundStereo(sound, stereo_position);
8467 }
8468
8469 void PlayMenuSoundIfLoopExt(int sound)
8470 {
8471   if (sound == SND_UNDEFINED)
8472     return;
8473
8474   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8475       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8476     return;
8477
8478   if (IS_LOOP_SOUND(sound))
8479     PlaySoundLoop(sound);
8480 }
8481
8482 void PlayMenuSoundIfLoop()
8483 {
8484   PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8485 }
8486
8487 void PlayMenuMusicExt(int music)
8488 {
8489   if (music == MUS_UNDEFINED)
8490     return;
8491
8492   if (!setup.sound_music)
8493     return;
8494
8495   PlayMusic(music);
8496 }
8497
8498 void PlayMenuMusic()
8499 {
8500   char *curr_music = getCurrentlyPlayingMusicFilename();
8501   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8502
8503   if (!strEqual(curr_music, next_music))
8504     PlayMenuMusicExt(menu.music[game_status]);
8505 }
8506
8507 void PlayMenuSoundsAndMusic()
8508 {
8509   PlayMenuSound();
8510   PlayMenuMusic();
8511 }
8512
8513 static void FadeMenuSounds()
8514 {
8515   FadeSounds();
8516 }
8517
8518 static void FadeMenuMusic()
8519 {
8520   char *curr_music = getCurrentlyPlayingMusicFilename();
8521   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8522
8523   if (!strEqual(curr_music, next_music))
8524     FadeMusic();
8525 }
8526
8527 void FadeMenuSoundsAndMusic()
8528 {
8529   FadeMenuSounds();
8530   FadeMenuMusic();
8531 }
8532
8533 void PlaySoundActivating()
8534 {
8535 #if 0
8536   PlaySound(SND_MENU_ITEM_ACTIVATING);
8537 #endif
8538 }
8539
8540 void PlaySoundSelecting()
8541 {
8542 #if 0
8543   PlaySound(SND_MENU_ITEM_SELECTING);
8544 #endif
8545 }
8546
8547 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8548 {
8549   boolean change_fullscreen = (setup.fullscreen !=
8550                                video.fullscreen_enabled);
8551   boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8552                                            setup.window_scaling_percent !=
8553                                            video.window_scaling_percent);
8554
8555   if (change_window_scaling_percent && video.fullscreen_enabled)
8556     return;
8557
8558   if (!change_window_scaling_percent && !video.fullscreen_available)
8559     return;
8560
8561 #if defined(TARGET_SDL2)
8562   if (change_window_scaling_percent)
8563   {
8564     SDLSetWindowScaling(setup.window_scaling_percent);
8565
8566     return;
8567   }
8568   else if (change_fullscreen)
8569   {
8570     SDLSetWindowFullscreen(setup.fullscreen);
8571
8572     /* set setup value according to successfully changed fullscreen mode */
8573     setup.fullscreen = video.fullscreen_enabled;
8574
8575     return;
8576   }
8577 #endif
8578
8579   if (change_fullscreen ||
8580       change_window_scaling_percent)
8581   {
8582     Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8583
8584     /* save backbuffer content which gets lost when toggling fullscreen mode */
8585     BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8586
8587     if (change_window_scaling_percent)
8588     {
8589       /* keep window mode, but change window scaling */
8590       video.fullscreen_enabled = TRUE;          /* force new window scaling */
8591     }
8592
8593     /* toggle fullscreen */
8594     ChangeVideoModeIfNeeded(setup.fullscreen);
8595
8596     /* set setup value according to successfully changed fullscreen mode */
8597     setup.fullscreen = video.fullscreen_enabled;
8598
8599     /* restore backbuffer content from temporary backbuffer backup bitmap */
8600     BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8601
8602     FreeBitmap(tmp_backbuffer);
8603
8604     /* update visible window/screen */
8605     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8606   }
8607 }
8608
8609 void JoinRectangles(int *x, int *y, int *width, int *height,
8610                     int x2, int y2, int width2, int height2)
8611 {
8612   // do not join with "off-screen" rectangle
8613   if (x2 == -1 || y2 == -1)
8614     return;
8615
8616   *x = MIN(*x, x2);
8617   *y = MIN(*y, y2);
8618   *width = MAX(*width, width2);
8619   *height = MAX(*height, height2);
8620 }
8621
8622 void SetAnimStatus(int anim_status_new)
8623 {
8624   if (anim_status_new == GAME_MODE_MAIN)
8625     anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
8626   else if (anim_status_new == GAME_MODE_SCORES)
8627     anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
8628
8629   global.anim_status_next = anim_status_new;
8630
8631   // directly set screen modes that are entered without fading
8632   if ((global.anim_status      == GAME_MODE_PSEUDO_MAINONLY &&
8633        global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
8634       (global.anim_status      == GAME_MODE_PSEUDO_TYPENAME &&
8635        global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
8636     global.anim_status = global.anim_status_next;
8637 }
8638
8639 void SetGameStatus(int game_status_new)
8640 {
8641   if (game_status_new != game_status)
8642     game_status_last_screen = game_status;
8643
8644   game_status = game_status_new;
8645
8646   SetAnimStatus(game_status_new);
8647 }
8648
8649 void SetFontStatus(int game_status_new)
8650 {
8651   static int last_game_status = -1;
8652
8653   if (game_status_new != -1)
8654   {
8655     // set game status for font use after storing last game status
8656     last_game_status = game_status;
8657     game_status = game_status_new;
8658   }
8659   else
8660   {
8661     // reset game status after font use from last stored game status
8662     game_status = last_game_status;
8663   }
8664 }
8665
8666 void ResetFontStatus()
8667 {
8668   SetFontStatus(-1);
8669 }
8670
8671 void ChangeViewportPropertiesIfNeeded()
8672 {
8673   int gfx_game_mode = game_status;
8674   int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8675                         game_status);
8676   struct RectWithBorder *vp_window    = &viewport.window[gfx_game_mode];
8677   struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8678   struct RectWithBorder *vp_door_1    = &viewport.door_1[gfx_game_mode];
8679   struct RectWithBorder *vp_door_2    = &viewport.door_2[gfx_game_mode2];
8680   struct RectWithBorder *vp_door_3    = &viewport.door_2[GAME_MODE_EDITOR];
8681   int new_win_xsize     = vp_window->width;
8682   int new_win_ysize     = vp_window->height;
8683   int border_size       = vp_playfield->border_size;
8684   int new_sx            = vp_playfield->x + border_size;
8685   int new_sy            = vp_playfield->y + border_size;
8686   int new_sxsize        = vp_playfield->width  - 2 * border_size;
8687   int new_sysize        = vp_playfield->height - 2 * border_size;
8688   int new_real_sx       = vp_playfield->x;
8689   int new_real_sy       = vp_playfield->y;
8690   int new_full_sxsize   = vp_playfield->width;
8691   int new_full_sysize   = vp_playfield->height;
8692   int new_dx            = vp_door_1->x;
8693   int new_dy            = vp_door_1->y;
8694   int new_dxsize        = vp_door_1->width;
8695   int new_dysize        = vp_door_1->height;
8696   int new_vx            = vp_door_2->x;
8697   int new_vy            = vp_door_2->y;
8698   int new_vxsize        = vp_door_2->width;
8699   int new_vysize        = vp_door_2->height;
8700   int new_ex            = vp_door_3->x;
8701   int new_ey            = vp_door_3->y;
8702   int new_exsize        = vp_door_3->width;
8703   int new_eysize        = vp_door_3->height;
8704   int new_tilesize_var =
8705     (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8706
8707   int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8708                   gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8709   int new_scr_fieldx = new_sxsize / tilesize;
8710   int new_scr_fieldy = new_sysize / tilesize;
8711   int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8712   int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8713   boolean init_gfx_buffers = FALSE;
8714   boolean init_video_buffer = FALSE;
8715   boolean init_gadgets_and_anims = FALSE;
8716   boolean init_em_graphics = FALSE;
8717
8718   if (new_win_xsize != WIN_XSIZE ||
8719       new_win_ysize != WIN_YSIZE)
8720   {
8721     WIN_XSIZE = new_win_xsize;
8722     WIN_YSIZE = new_win_ysize;
8723
8724     init_video_buffer = TRUE;
8725     init_gfx_buffers = TRUE;
8726     init_gadgets_and_anims = TRUE;
8727
8728     // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8729   }
8730
8731   if (new_scr_fieldx != SCR_FIELDX ||
8732       new_scr_fieldy != SCR_FIELDY)
8733   {
8734     /* this always toggles between MAIN and GAME when using small tile size */
8735
8736     SCR_FIELDX = new_scr_fieldx;
8737     SCR_FIELDY = new_scr_fieldy;
8738
8739     // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8740   }
8741
8742   if (new_sx != SX ||
8743       new_sy != SY ||
8744       new_dx != DX ||
8745       new_dy != DY ||
8746       new_vx != VX ||
8747       new_vy != VY ||
8748       new_ex != EX ||
8749       new_ey != EY ||
8750       new_sxsize != SXSIZE ||
8751       new_sysize != SYSIZE ||
8752       new_dxsize != DXSIZE ||
8753       new_dysize != DYSIZE ||
8754       new_vxsize != VXSIZE ||
8755       new_vysize != VYSIZE ||
8756       new_exsize != EXSIZE ||
8757       new_eysize != EYSIZE ||
8758       new_real_sx != REAL_SX ||
8759       new_real_sy != REAL_SY ||
8760       new_full_sxsize != FULL_SXSIZE ||
8761       new_full_sysize != FULL_SYSIZE ||
8762       new_tilesize_var != TILESIZE_VAR
8763       )
8764   {
8765     // ------------------------------------------------------------------------
8766     // determine next fading area for changed viewport definitions
8767     // ------------------------------------------------------------------------
8768
8769     // start with current playfield area (default fading area)
8770     FADE_SX = REAL_SX;
8771     FADE_SY = REAL_SY;
8772     FADE_SXSIZE = FULL_SXSIZE;
8773     FADE_SYSIZE = FULL_SYSIZE;
8774
8775     // add new playfield area if position or size has changed
8776     if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8777         new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8778     {
8779       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8780                      new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8781     }
8782
8783     // add current and new door 1 area if position or size has changed
8784     if (new_dx != DX || new_dy != DY ||
8785         new_dxsize != DXSIZE || new_dysize != DYSIZE)
8786     {
8787       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8788                      DX, DY, DXSIZE, DYSIZE);
8789       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8790                      new_dx, new_dy, new_dxsize, new_dysize);
8791     }
8792
8793     // add current and new door 2 area if position or size has changed
8794     if (new_dx != VX || new_dy != VY ||
8795         new_dxsize != VXSIZE || new_dysize != VYSIZE)
8796     {
8797       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8798                      VX, VY, VXSIZE, VYSIZE);
8799       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8800                      new_vx, new_vy, new_vxsize, new_vysize);
8801     }
8802
8803     // ------------------------------------------------------------------------
8804     // handle changed tile size
8805     // ------------------------------------------------------------------------
8806
8807     if (new_tilesize_var != TILESIZE_VAR)
8808     {
8809       // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8810
8811       // changing tile size invalidates scroll values of engine snapshots
8812       FreeEngineSnapshotSingle();
8813
8814       // changing tile size requires update of graphic mapping for EM engine
8815       init_em_graphics = TRUE;
8816     }
8817
8818     SX = new_sx;
8819     SY = new_sy;
8820     DX = new_dx;
8821     DY = new_dy;
8822     VX = new_vx;
8823     VY = new_vy;
8824     EX = new_ex;
8825     EY = new_ey;
8826     SXSIZE = new_sxsize;
8827     SYSIZE = new_sysize;
8828     DXSIZE = new_dxsize;
8829     DYSIZE = new_dysize;
8830     VXSIZE = new_vxsize;
8831     VYSIZE = new_vysize;
8832     EXSIZE = new_exsize;
8833     EYSIZE = new_eysize;
8834     REAL_SX = new_real_sx;
8835     REAL_SY = new_real_sy;
8836     FULL_SXSIZE = new_full_sxsize;
8837     FULL_SYSIZE = new_full_sysize;
8838     TILESIZE_VAR = new_tilesize_var;
8839
8840     init_gfx_buffers = TRUE;
8841     init_gadgets_and_anims = TRUE;
8842
8843     // printf("::: viewports: init_gfx_buffers\n");
8844     // printf("::: viewports: init_gadgets_and_anims\n");
8845   }
8846
8847   if (init_gfx_buffers)
8848   {
8849     // printf("::: init_gfx_buffers\n");
8850
8851     SCR_FIELDX = new_scr_fieldx_buffers;
8852     SCR_FIELDY = new_scr_fieldy_buffers;
8853
8854     InitGfxBuffers();
8855
8856     SCR_FIELDX = new_scr_fieldx;
8857     SCR_FIELDY = new_scr_fieldy;
8858
8859     SetDrawDeactivationMask(REDRAW_NONE);
8860     SetDrawBackgroundMask(REDRAW_FIELD);
8861   }
8862
8863   if (init_video_buffer)
8864   {
8865     // printf("::: init_video_buffer\n");
8866
8867     InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8868     InitImageTextures();
8869   }
8870
8871   if (init_gadgets_and_anims)
8872   {
8873     // printf("::: init_gadgets_and_anims\n");
8874
8875     InitGadgets();
8876     InitGlobalAnimations();
8877   }
8878
8879   if (init_em_graphics)
8880   {
8881       InitGraphicInfo_EM();
8882   }
8883 }