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