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