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