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