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