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