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