63bff391ce32a9c3c82b0dba25dfa8a863abf79a
[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 DrawPreviewPlayers()
3508 {
3509   if (game_status != GAME_MODE_MAIN)
3510     return;
3511
3512   if (!network.enabled && !setup.team_mode)
3513     return;
3514
3515   boolean player_found[MAX_PLAYERS];
3516   int num_players = 0;
3517   int i, x, y;
3518
3519   for (i = 0; i < MAX_PLAYERS; i++)
3520     player_found[i] = FALSE;
3521
3522   /* check which players can be found in the level (simple approach) */
3523   for (x = 0; x < lev_fieldx; x++)
3524   {
3525     for (y = 0; y < lev_fieldy; y++)
3526     {
3527       int element = level.field[x][y];
3528
3529       if (ELEM_IS_PLAYER(element))
3530       {
3531         int player_nr = GET_PLAYER_NR(element);
3532
3533         player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3534
3535         if (!player_found[player_nr])
3536           num_players++;
3537
3538         player_found[player_nr] = TRUE;
3539       }
3540     }
3541   }
3542
3543   struct TextPosInfo *pos = &menu.main.preview_players;
3544   int tile_size = pos->tile_size;
3545   int border_size = pos->border_size;
3546   int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3547   int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3548   int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3549   int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3550   int max_players_width  = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3551   int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3552   int all_players_width  = (num_players - 1) * player_xoffset + tile_size;
3553   int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3554   int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width,  pos->align);
3555   int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3556   int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width,  pos->align);
3557   int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3558
3559   /* clear area in which the players will be drawn */
3560   ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3561                              max_players_width, max_players_height);
3562
3563   /* only draw players if level is suited for team mode */
3564   if (num_players < 2)
3565     return;
3566
3567   /* draw all players that were found in the level */
3568   for (i = 0; i < MAX_PLAYERS; i++)
3569   {
3570     if (player_found[i])
3571     {
3572       int graphic = el2img(EL_PLAYER_1 + i);
3573
3574       DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3575
3576       xpos += player_xoffset;
3577       ypos += player_yoffset;
3578     }
3579   }
3580 }
3581
3582 void DrawPreviewLevelInitial()
3583 {
3584   DrawPreviewLevelExt(TRUE);
3585   DrawPreviewPlayers();
3586 }
3587
3588 void DrawPreviewLevelAnimation()
3589 {
3590   DrawPreviewLevelExt(FALSE);
3591 }
3592
3593 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3594                               int border_size, int font_nr)
3595 {
3596   int graphic = el2img(EL_PLAYER_1 + player_nr);
3597   int font_height = getFontHeight(font_nr);
3598   int player_height = MAX(tile_size, font_height);
3599   int xoffset_text = tile_size + border_size;
3600   int yoffset_text    = (player_height - font_height) / 2;
3601   int yoffset_graphic = (player_height - tile_size) / 2;
3602   char *player_name = getNetworkPlayerName(player_nr + 1);
3603
3604   DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3605                               tile_size);
3606   DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3607 }
3608
3609 void DrawNetworkPlayersExt(boolean force)
3610 {
3611   if (game_status != GAME_MODE_MAIN)
3612     return;
3613
3614   if (!network.connected && !force)
3615     return;
3616
3617   int num_players = 0;
3618   int i;
3619
3620   for (i = 0; i < MAX_PLAYERS; i++)
3621     if (stored_player[i].connected_network)
3622       num_players++;
3623
3624   struct TextPosInfo *pos = &menu.main.network_players;
3625   int tile_size = pos->tile_size;
3626   int border_size = pos->border_size;
3627   int xoffset_text = tile_size + border_size;
3628   int font_nr = pos->font;
3629   int font_width = getFontWidth(font_nr);
3630   int font_height = getFontHeight(font_nr);
3631   int player_height = MAX(tile_size, font_height);
3632   int player_yoffset = player_height + border_size;
3633   int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3634   int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3635   int all_players_height = num_players * player_yoffset - border_size;
3636   int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width,  pos->align);
3637   int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3638   int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3639
3640   ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3641                              max_players_width, max_players_height);
3642
3643   /* first draw local network player ... */
3644   for (i = 0; i < MAX_PLAYERS; i++)
3645   {
3646     if (stored_player[i].connected_network &&
3647         stored_player[i].connected_locally)
3648     {
3649       char *player_name = getNetworkPlayerName(i + 1);
3650       int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3651       int xpos = SX + ALIGNED_XPOS(pos->x, player_width,  pos->align);
3652
3653       DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3654
3655       ypos += player_yoffset;
3656     }
3657   }
3658
3659   /* ... then draw all other network players */
3660   for (i = 0; i < MAX_PLAYERS; i++)
3661   {
3662     if (stored_player[i].connected_network &&
3663         !stored_player[i].connected_locally)
3664     {
3665       char *player_name = getNetworkPlayerName(i + 1);
3666       int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3667       int xpos = SX + ALIGNED_XPOS(pos->x, player_width,  pos->align);
3668
3669       DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3670
3671       ypos += player_yoffset;
3672     }
3673   }
3674 }
3675
3676 void DrawNetworkPlayers()
3677 {
3678   DrawNetworkPlayersExt(FALSE);
3679 }
3680
3681 void ClearNetworkPlayers()
3682 {
3683   DrawNetworkPlayersExt(TRUE);
3684 }
3685
3686 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3687                                            int graphic, int sync_frame,
3688                                            int mask_mode)
3689 {
3690   int frame = getGraphicAnimationFrame(graphic, sync_frame);
3691
3692   if (mask_mode == USE_MASKING)
3693     DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3694   else
3695     DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3696 }
3697
3698 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3699                                   int graphic, int sync_frame, int mask_mode)
3700 {
3701   int frame = getGraphicAnimationFrame(graphic, sync_frame);
3702
3703   if (mask_mode == USE_MASKING)
3704     DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3705   else
3706     DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3707 }
3708
3709 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3710 {
3711   int lx = LEVELX(x), ly = LEVELY(y);
3712
3713   if (!IN_SCR_FIELD(x, y))
3714     return;
3715
3716   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3717                           graphic, GfxFrame[lx][ly], NO_MASKING);
3718
3719   MarkTileDirty(x, y);
3720 }
3721
3722 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3723 {
3724   int lx = LEVELX(x), ly = LEVELY(y);
3725
3726   if (!IN_SCR_FIELD(x, y))
3727     return;
3728
3729   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3730                           graphic, GfxFrame[lx][ly], NO_MASKING);
3731   MarkTileDirty(x, y);
3732 }
3733
3734 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3735 {
3736   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3737 }
3738
3739 void DrawLevelElementAnimation(int x, int y, int element)
3740 {
3741   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3742
3743   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3744 }
3745
3746 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3747 {
3748   int sx = SCREENX(x), sy = SCREENY(y);
3749
3750   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3751     return;
3752
3753   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3754     return;
3755
3756   DrawGraphicAnimation(sx, sy, graphic);
3757
3758 #if 1
3759   if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3760     DrawLevelFieldCrumbled(x, y);
3761 #else
3762   if (GFX_CRUMBLED(Feld[x][y]))
3763     DrawLevelFieldCrumbled(x, y);
3764 #endif
3765 }
3766
3767 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3768 {
3769   int sx = SCREENX(x), sy = SCREENY(y);
3770   int graphic;
3771
3772   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3773     return;
3774
3775   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3776
3777   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3778     return;
3779
3780   DrawGraphicAnimation(sx, sy, graphic);
3781
3782   if (GFX_CRUMBLED(element))
3783     DrawLevelFieldCrumbled(x, y);
3784 }
3785
3786 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3787 {
3788   if (player->use_murphy)
3789   {
3790     /* this works only because currently only one player can be "murphy" ... */
3791     static int last_horizontal_dir = MV_LEFT;
3792     int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3793
3794     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3795       last_horizontal_dir = move_dir;
3796
3797     if (graphic == IMG_SP_MURPHY)       /* undefined => use special graphic */
3798     {
3799       int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3800
3801       graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3802     }
3803
3804     return graphic;
3805   }
3806   else
3807     return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3808 }
3809
3810 static boolean equalGraphics(int graphic1, int graphic2)
3811 {
3812   struct GraphicInfo *g1 = &graphic_info[graphic1];
3813   struct GraphicInfo *g2 = &graphic_info[graphic2];
3814
3815   return (g1->bitmap      == g2->bitmap &&
3816           g1->src_x       == g2->src_x &&
3817           g1->src_y       == g2->src_y &&
3818           g1->anim_frames == g2->anim_frames &&
3819           g1->anim_delay  == g2->anim_delay &&
3820           g1->anim_mode   == g2->anim_mode);
3821 }
3822
3823 void DrawAllPlayers()
3824 {
3825   int i;
3826
3827   for (i = 0; i < MAX_PLAYERS; i++)
3828     if (stored_player[i].active)
3829       DrawPlayer(&stored_player[i]);
3830 }
3831
3832 void DrawPlayerField(int x, int y)
3833 {
3834   if (!IS_PLAYER(x, y))
3835     return;
3836
3837   DrawPlayer(PLAYERINFO(x, y));
3838 }
3839
3840 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3841
3842 void DrawPlayer(struct PlayerInfo *player)
3843 {
3844   int jx = player->jx;
3845   int jy = player->jy;
3846   int move_dir = player->MovDir;
3847   int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3848   int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? +1 : 0);
3849   int last_jx = (player->is_moving ? jx - dx : jx);
3850   int last_jy = (player->is_moving ? jy - dy : jy);
3851   int next_jx = jx + dx;
3852   int next_jy = jy + dy;
3853   boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3854   boolean player_is_opaque = FALSE;
3855   int sx = SCREENX(jx), sy = SCREENY(jy);
3856   int sxx = 0, syy = 0;
3857   int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3858   int graphic;
3859   int action = ACTION_DEFAULT;
3860   int last_player_graphic = getPlayerGraphic(player, move_dir);
3861   int last_player_frame = player->Frame;
3862   int frame = 0;
3863
3864   /* GfxElement[][] is set to the element the player is digging or collecting;
3865      remove also for off-screen player if the player is not moving anymore */
3866   if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3867     GfxElement[jx][jy] = EL_UNDEFINED;
3868
3869   if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3870     return;
3871
3872 #if DEBUG
3873   if (!IN_LEV_FIELD(jx, jy))
3874   {
3875     printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3876     printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3877     printf("DrawPlayerField(): This should never happen!\n");
3878     return;
3879   }
3880 #endif
3881
3882   if (element == EL_EXPLOSION)
3883     return;
3884
3885   action = (player->is_pushing    ? ACTION_PUSHING         :
3886             player->is_digging    ? ACTION_DIGGING         :
3887             player->is_collecting ? ACTION_COLLECTING      :
3888             player->is_moving     ? ACTION_MOVING          :
3889             player->is_snapping   ? ACTION_SNAPPING        :
3890             player->is_dropping   ? ACTION_DROPPING        :
3891             player->is_waiting    ? player->action_waiting : ACTION_DEFAULT);
3892
3893   if (player->is_waiting)
3894     move_dir = player->dir_waiting;
3895
3896   InitPlayerGfxAnimation(player, action, move_dir);
3897
3898   /* ----------------------------------------------------------------------- */
3899   /* draw things in the field the player is leaving, if needed               */
3900   /* ----------------------------------------------------------------------- */
3901
3902   if (player->is_moving)
3903   {
3904     if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3905     {
3906       DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3907
3908       if (last_element == EL_DYNAMITE_ACTIVE ||
3909           last_element == EL_EM_DYNAMITE_ACTIVE ||
3910           last_element == EL_SP_DISK_RED_ACTIVE)
3911         DrawDynamite(last_jx, last_jy);
3912       else
3913         DrawLevelFieldThruMask(last_jx, last_jy);
3914     }
3915     else if (last_element == EL_DYNAMITE_ACTIVE ||
3916              last_element == EL_EM_DYNAMITE_ACTIVE ||
3917              last_element == EL_SP_DISK_RED_ACTIVE)
3918       DrawDynamite(last_jx, last_jy);
3919 #if 0
3920     /* !!! this is not enough to prevent flickering of players which are
3921        moving next to each others without a free tile between them -- this
3922        can only be solved by drawing all players layer by layer (first the
3923        background, then the foreground etc.) !!! => TODO */
3924     else if (!IS_PLAYER(last_jx, last_jy))
3925       DrawLevelField(last_jx, last_jy);
3926 #else
3927     else
3928       DrawLevelField(last_jx, last_jy);
3929 #endif
3930
3931     if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3932       DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3933   }
3934
3935   if (!IN_SCR_FIELD(sx, sy))
3936     return;
3937
3938   /* ----------------------------------------------------------------------- */
3939   /* draw things behind the player, if needed                                */
3940   /* ----------------------------------------------------------------------- */
3941
3942   if (Back[jx][jy])
3943     DrawLevelElement(jx, jy, Back[jx][jy]);
3944   else if (IS_ACTIVE_BOMB(element))
3945     DrawLevelElement(jx, jy, EL_EMPTY);
3946   else
3947   {
3948     if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3949     {
3950       int old_element = GfxElement[jx][jy];
3951       int old_graphic = el_act_dir2img(old_element, action, move_dir);
3952       int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3953
3954       if (GFX_CRUMBLED(old_element))
3955         DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3956       else
3957         DrawGraphic(sx, sy, old_graphic, frame);
3958
3959       if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3960         player_is_opaque = TRUE;
3961     }
3962     else
3963     {
3964       GfxElement[jx][jy] = EL_UNDEFINED;
3965
3966       /* make sure that pushed elements are drawn with correct frame rate */
3967       graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3968
3969       if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3970         GfxFrame[jx][jy] = player->StepFrame;
3971
3972       DrawLevelField(jx, jy);
3973     }
3974   }
3975
3976 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3977   /* ----------------------------------------------------------------------- */
3978   /* draw player himself                                                     */
3979   /* ----------------------------------------------------------------------- */
3980
3981   graphic = getPlayerGraphic(player, move_dir);
3982
3983   /* in the case of changed player action or direction, prevent the current
3984      animation frame from being restarted for identical animations */
3985   if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3986     player->Frame = last_player_frame;
3987
3988   frame = getGraphicAnimationFrame(graphic, player->Frame);
3989
3990   if (player->GfxPos)
3991   {
3992     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3993       sxx = player->GfxPos;
3994     else
3995       syy = player->GfxPos;
3996   }
3997
3998   if (player_is_opaque)
3999     DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
4000   else
4001     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4002
4003   if (SHIELD_ON(player))
4004   {
4005     int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4006                    IMG_SHIELD_NORMAL_ACTIVE);
4007     int frame = getGraphicAnimationFrame(graphic, -1);
4008
4009     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4010   }
4011 #endif
4012
4013 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4014   if (player->GfxPos)
4015   {
4016     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4017       sxx = player->GfxPos;
4018     else
4019       syy = player->GfxPos;
4020   }
4021 #endif
4022
4023   /* ----------------------------------------------------------------------- */
4024   /* draw things the player is pushing, if needed                            */
4025   /* ----------------------------------------------------------------------- */
4026
4027   if (player->is_pushing && player->is_moving)
4028   {
4029     int px = SCREENX(jx), py = SCREENY(jy);
4030     int pxx = (TILEX - ABS(sxx)) * dx;
4031     int pyy = (TILEY - ABS(syy)) * dy;
4032     int gfx_frame = GfxFrame[jx][jy];
4033
4034     int graphic;
4035     int sync_frame;
4036     int frame;
4037
4038     if (!IS_MOVING(jx, jy))             /* push movement already finished */
4039     {
4040       element = Feld[next_jx][next_jy];
4041       gfx_frame = GfxFrame[next_jx][next_jy];
4042     }
4043
4044     graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4045
4046     sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4047     frame = getGraphicAnimationFrame(graphic, sync_frame);
4048
4049     /* draw background element under pushed element (like the Sokoban field) */
4050     if (game.use_masked_pushing && IS_MOVING(jx, jy))
4051     {
4052       /* this allows transparent pushing animation over non-black background */
4053
4054       if (Back[jx][jy])
4055         DrawLevelElement(jx, jy, Back[jx][jy]);
4056       else
4057         DrawLevelElement(jx, jy, EL_EMPTY);
4058
4059       if (Back[next_jx][next_jy])
4060         DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4061       else
4062         DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4063     }
4064     else if (Back[next_jx][next_jy])
4065       DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4066
4067 #if 1
4068     /* do not draw (EM style) pushing animation when pushing is finished */
4069     /* (two-tile animations usually do not contain start and end frame) */
4070     if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4071       DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
4072     else
4073       DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4074 #else
4075     /* masked drawing is needed for EMC style (double) movement graphics */
4076     /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
4077     DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4078 #endif
4079   }
4080
4081 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4082   /* ----------------------------------------------------------------------- */
4083   /* draw player himself                                                     */
4084   /* ----------------------------------------------------------------------- */
4085
4086   graphic = getPlayerGraphic(player, move_dir);
4087
4088   /* in the case of changed player action or direction, prevent the current
4089      animation frame from being restarted for identical animations */
4090   if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4091     player->Frame = last_player_frame;
4092
4093   frame = getGraphicAnimationFrame(graphic, player->Frame);
4094
4095   if (player->GfxPos)
4096   {
4097     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4098       sxx = player->GfxPos;
4099     else
4100       syy = player->GfxPos;
4101   }
4102
4103   if (player_is_opaque)
4104     DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
4105   else
4106     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4107
4108   if (SHIELD_ON(player))
4109   {
4110     int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4111                    IMG_SHIELD_NORMAL_ACTIVE);
4112     int frame = getGraphicAnimationFrame(graphic, -1);
4113
4114     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4115   }
4116 #endif
4117
4118   /* ----------------------------------------------------------------------- */
4119   /* draw things in front of player (active dynamite or dynabombs)           */
4120   /* ----------------------------------------------------------------------- */
4121
4122   if (IS_ACTIVE_BOMB(element))
4123   {
4124     graphic = el2img(element);
4125     frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4126
4127     if (game.emulation == EMU_SUPAPLEX)
4128       DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4129     else
4130       DrawGraphicThruMask(sx, sy, graphic, frame);
4131   }
4132
4133   if (player_is_moving && last_element == EL_EXPLOSION)
4134   {
4135     int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4136                    GfxElement[last_jx][last_jy] :  EL_EMPTY);
4137     int graphic = el_act2img(element, ACTION_EXPLODING);
4138     int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4139     int phase = ExplodePhase[last_jx][last_jy] - 1;
4140     int frame = getGraphicAnimationFrame(graphic, phase - delay);
4141
4142     if (phase >= delay)
4143       DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4144   }
4145
4146   /* ----------------------------------------------------------------------- */
4147   /* draw elements the player is just walking/passing through/under          */
4148   /* ----------------------------------------------------------------------- */
4149
4150   if (player_is_moving)
4151   {
4152     /* handle the field the player is leaving ... */
4153     if (IS_ACCESSIBLE_INSIDE(last_element))
4154       DrawLevelField(last_jx, last_jy);
4155     else if (IS_ACCESSIBLE_UNDER(last_element))
4156       DrawLevelFieldThruMask(last_jx, last_jy);
4157   }
4158
4159   /* do not redraw accessible elements if the player is just pushing them */
4160   if (!player_is_moving || !player->is_pushing)
4161   {
4162     /* ... and the field the player is entering */
4163     if (IS_ACCESSIBLE_INSIDE(element))
4164       DrawLevelField(jx, jy);
4165     else if (IS_ACCESSIBLE_UNDER(element))
4166       DrawLevelFieldThruMask(jx, jy);
4167   }
4168
4169   MarkTileDirty(sx, sy);
4170 }
4171
4172 /* ------------------------------------------------------------------------- */
4173
4174 void WaitForEventToContinue()
4175 {
4176   boolean still_wait = TRUE;
4177
4178   if (program.headless)
4179     return;
4180
4181   /* simulate releasing mouse button over last gadget, if still pressed */
4182   if (button_status)
4183     HandleGadgets(-1, -1, 0);
4184
4185   button_status = MB_RELEASED;
4186
4187   ClearEventQueue();
4188
4189   while (still_wait)
4190   {
4191     Event event;
4192
4193     if (NextValidEvent(&event))
4194     {
4195       switch (event.type)
4196       {
4197         case EVENT_BUTTONPRESS:
4198         case EVENT_KEYPRESS:
4199 #if defined(TARGET_SDL2)
4200         case SDL_CONTROLLERBUTTONDOWN:
4201 #endif
4202         case SDL_JOYBUTTONDOWN:
4203           still_wait = FALSE;
4204           break;
4205
4206         case EVENT_KEYRELEASE:
4207           ClearPlayerAction();
4208           break;
4209
4210         default:
4211           HandleOtherEvents(&event);
4212           break;
4213       }
4214     }
4215     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4216     {
4217       still_wait = FALSE;
4218     }
4219
4220     BackToFront();
4221   }
4222 }
4223
4224 #define MAX_REQUEST_LINES               13
4225 #define MAX_REQUEST_LINE_FONT1_LEN      7
4226 #define MAX_REQUEST_LINE_FONT2_LEN      10
4227
4228 static int RequestHandleEvents(unsigned int req_state)
4229 {
4230   boolean level_solved = (game_status == GAME_MODE_PLAYING &&
4231                           local_player->LevelSolved_GameEnd);
4232   int width  = request.width;
4233   int height = request.height;
4234   int sx, sy;
4235   int result;
4236
4237   setRequestPosition(&sx, &sy, FALSE);
4238
4239   button_status = MB_RELEASED;
4240
4241   request_gadget_id = -1;
4242   result = -1;
4243
4244   while (result < 0)
4245   {
4246     if (level_solved)
4247     {
4248       /* the MM game engine does not use a special (scrollable) field buffer */
4249       if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4250         SetDrawtoField(DRAW_TO_FIELDBUFFER);
4251
4252       HandleGameActions();
4253
4254       SetDrawtoField(DRAW_TO_BACKBUFFER);
4255
4256       if (global.use_envelope_request)
4257       {
4258         /* copy current state of request area to middle of playfield area */
4259         BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4260       }
4261     }
4262
4263     if (PendingEvent())
4264     {
4265       Event event;
4266
4267       while (NextValidEvent(&event))
4268       {
4269         switch (event.type)
4270         {
4271           case EVENT_BUTTONPRESS:
4272           case EVENT_BUTTONRELEASE:
4273           case EVENT_MOTIONNOTIFY:
4274           {
4275             int mx, my;
4276
4277             if (event.type == EVENT_MOTIONNOTIFY)
4278             {
4279               if (!button_status)
4280                 continue;
4281
4282               motion_status = TRUE;
4283               mx = ((MotionEvent *) &event)->x;
4284               my = ((MotionEvent *) &event)->y;
4285             }
4286             else
4287             {
4288               motion_status = FALSE;
4289               mx = ((ButtonEvent *) &event)->x;
4290               my = ((ButtonEvent *) &event)->y;
4291               if (event.type == EVENT_BUTTONPRESS)
4292                 button_status = ((ButtonEvent *) &event)->button;
4293               else
4294                 button_status = MB_RELEASED;
4295             }
4296
4297             /* this sets 'request_gadget_id' */
4298             HandleGadgets(mx, my, button_status);
4299
4300             switch (request_gadget_id)
4301             {
4302               case TOOL_CTRL_ID_YES:
4303                 result = TRUE;
4304                 break;
4305               case TOOL_CTRL_ID_NO:
4306                 result = FALSE;
4307                 break;
4308               case TOOL_CTRL_ID_CONFIRM:
4309                 result = TRUE | FALSE;
4310                 break;
4311
4312               case TOOL_CTRL_ID_PLAYER_1:
4313                 result = 1;
4314                 break;
4315               case TOOL_CTRL_ID_PLAYER_2:
4316                 result = 2;
4317                 break;
4318               case TOOL_CTRL_ID_PLAYER_3:
4319                 result = 3;
4320                 break;
4321               case TOOL_CTRL_ID_PLAYER_4:
4322                 result = 4;
4323                 break;
4324
4325               default:
4326                 break;
4327             }
4328
4329             break;
4330           }
4331
4332 #if defined(TARGET_SDL2)
4333           case SDL_WINDOWEVENT:
4334             HandleWindowEvent((WindowEvent *) &event);
4335             break;
4336
4337           case SDL_APP_WILLENTERBACKGROUND:
4338           case SDL_APP_DIDENTERBACKGROUND:
4339           case SDL_APP_WILLENTERFOREGROUND:
4340           case SDL_APP_DIDENTERFOREGROUND:
4341             HandlePauseResumeEvent((PauseResumeEvent *) &event);
4342             break;
4343 #endif
4344
4345           case EVENT_KEYPRESS:
4346           {
4347             Key key = GetEventKey((KeyEvent *)&event, TRUE);
4348
4349             switch (key)
4350             {
4351               case KSYM_space:
4352                 if (req_state & REQ_CONFIRM)
4353                   result = 1;
4354                 break;
4355
4356               case KSYM_Return:
4357               case KSYM_y:
4358 #if defined(TARGET_SDL2)
4359               case KSYM_Y:
4360               case KSYM_Select:
4361               case KSYM_Menu:
4362 #if defined(KSYM_Rewind)
4363               case KSYM_Rewind:         /* for Amazon Fire TV remote */
4364 #endif
4365 #endif
4366                 result = 1;
4367                 break;
4368
4369               case KSYM_Escape:
4370               case KSYM_n:
4371 #if defined(TARGET_SDL2)
4372               case KSYM_N:
4373               case KSYM_Back:
4374 #if defined(KSYM_FastForward)
4375               case KSYM_FastForward:    /* for Amazon Fire TV remote */
4376 #endif
4377 #endif
4378                 result = 0;
4379                 break;
4380
4381               default:
4382                 HandleKeysDebug(key);
4383                 break;
4384             }
4385
4386             if (req_state & REQ_PLAYER)
4387             {
4388               int old_player_nr = setup.network_player_nr;
4389
4390               if (result != -1)
4391                 result = old_player_nr + 1;
4392
4393               switch (key)
4394               {
4395                 case KSYM_space:
4396                   result = old_player_nr + 1;
4397                   break;
4398
4399                 case KSYM_Up:
4400                 case KSYM_1:
4401                   result = 1;
4402                   break;
4403
4404                 case KSYM_Right:
4405                 case KSYM_2:
4406                   result = 2;
4407                   break;
4408
4409                 case KSYM_Down:
4410                 case KSYM_3:
4411                   result = 3;
4412                   break;
4413
4414                 case KSYM_Left:
4415                 case KSYM_4:
4416                   result = 4;
4417                   break;
4418
4419                 default:
4420                   break;
4421               }
4422             }
4423
4424             break;
4425           }
4426
4427           case EVENT_KEYRELEASE:
4428             ClearPlayerAction();
4429             break;
4430
4431 #if defined(TARGET_SDL2)
4432           case SDL_CONTROLLERBUTTONDOWN:
4433             switch (event.cbutton.button)
4434             {
4435               case SDL_CONTROLLER_BUTTON_A:
4436               case SDL_CONTROLLER_BUTTON_X:
4437               case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4438               case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4439                 result = 1;
4440                 break;
4441
4442               case SDL_CONTROLLER_BUTTON_B:
4443               case SDL_CONTROLLER_BUTTON_Y:
4444               case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4445               case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4446               case SDL_CONTROLLER_BUTTON_BACK:
4447                 result = 0;
4448                 break;
4449             }
4450
4451             if (req_state & REQ_PLAYER)
4452             {
4453               int old_player_nr = setup.network_player_nr;
4454
4455               if (result != -1)
4456                 result = old_player_nr + 1;
4457
4458               switch (event.cbutton.button)
4459               {
4460                 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4461                 case SDL_CONTROLLER_BUTTON_Y:
4462                   result = 1;
4463                   break;
4464
4465                 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4466                 case SDL_CONTROLLER_BUTTON_B:
4467                   result = 2;
4468                   break;
4469
4470                 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4471                 case SDL_CONTROLLER_BUTTON_A:
4472                   result = 3;
4473                   break;
4474
4475                 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4476                 case SDL_CONTROLLER_BUTTON_X:
4477                   result = 4;
4478                   break;
4479
4480                 default:
4481                   break;
4482               }
4483             }
4484
4485             break;
4486
4487           case SDL_CONTROLLERBUTTONUP:
4488             HandleJoystickEvent(&event);
4489             ClearPlayerAction();
4490             break;
4491 #endif
4492
4493           default:
4494             HandleOtherEvents(&event);
4495             break;
4496         }
4497       }
4498     }
4499     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4500     {
4501       int joy = AnyJoystick();
4502
4503       if (joy & JOY_BUTTON_1)
4504         result = 1;
4505       else if (joy & JOY_BUTTON_2)
4506         result = 0;
4507     }
4508     else if (AnyJoystick())
4509     {
4510       int joy = AnyJoystick();
4511
4512       if (req_state & REQ_PLAYER)
4513       {
4514         if (joy & JOY_UP)
4515           result = 1;
4516         else if (joy & JOY_RIGHT)
4517           result = 2;
4518         else if (joy & JOY_DOWN)
4519           result = 3;
4520         else if (joy & JOY_LEFT)
4521           result = 4;
4522       }
4523     }
4524
4525     if (level_solved)
4526     {
4527       if (global.use_envelope_request)
4528       {
4529         /* copy back current state of pressed buttons inside request area */
4530         BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4531       }
4532     }
4533
4534     BackToFront();
4535   }
4536
4537   return result;
4538 }
4539
4540 static boolean RequestDoor(char *text, unsigned int req_state)
4541 {
4542   unsigned int old_door_state;
4543   int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4544   int font_nr = FONT_TEXT_2;
4545   char *text_ptr;
4546   int result;
4547   int ty;
4548
4549   if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4550   {
4551     max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4552     font_nr = FONT_TEXT_1;
4553   }
4554
4555   if (game_status == GAME_MODE_PLAYING)
4556     BlitScreenToBitmap(backbuffer);
4557
4558   /* disable deactivated drawing when quick-loading level tape recording */
4559   if (tape.playing && tape.deactivate_display)
4560     TapeDeactivateDisplayOff(TRUE);
4561
4562   SetMouseCursor(CURSOR_DEFAULT);
4563
4564   /* pause network game while waiting for request to answer */
4565   if (network.enabled &&
4566       game_status == GAME_MODE_PLAYING &&
4567       req_state & REQUEST_WAIT_FOR_INPUT)
4568     SendToServer_PausePlaying();
4569
4570   old_door_state = GetDoorState();
4571
4572   /* simulate releasing mouse button over last gadget, if still pressed */
4573   if (button_status)
4574     HandleGadgets(-1, -1, 0);
4575
4576   UnmapAllGadgets();
4577
4578   /* draw released gadget before proceeding */
4579   // BackToFront();
4580
4581   if (old_door_state & DOOR_OPEN_1)
4582   {
4583     CloseDoor(DOOR_CLOSE_1);
4584
4585     /* save old door content */
4586     BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4587                0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4588   }
4589
4590   SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4591   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4592
4593   /* clear door drawing field */
4594   DrawBackground(DX, DY, DXSIZE, DYSIZE);
4595
4596   /* force DOOR font inside door area */
4597   SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4598
4599   /* write text for request */
4600   for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4601   {
4602     char text_line[max_request_line_len + 1];
4603     int tx, tl, tc = 0;
4604
4605     if (!*text_ptr)
4606       break;
4607
4608     for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4609     {
4610       tc = *(text_ptr + tx);
4611       // if (!tc || tc == ' ')
4612       if (!tc || tc == ' ' || tc == '?' || tc == '!')
4613         break;
4614     }
4615
4616     if ((tc == '?' || tc == '!') && tl == 0)
4617       tl = 1;
4618
4619     if (!tl)
4620     { 
4621       text_ptr++; 
4622       ty--; 
4623       continue; 
4624     }
4625
4626     strncpy(text_line, text_ptr, tl);
4627     text_line[tl] = 0;
4628
4629     DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4630              DY + 8 + ty * (getFontHeight(font_nr) + 2),
4631              text_line, font_nr);
4632
4633     text_ptr += tl + (tc == ' ' ? 1 : 0);
4634     // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4635   }
4636
4637   ResetFontStatus();
4638
4639   if (req_state & REQ_ASK)
4640   {
4641     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4642     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4643   }
4644   else if (req_state & REQ_CONFIRM)
4645   {
4646     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4647   }
4648   else if (req_state & REQ_PLAYER)
4649   {
4650     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4651     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4652     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4653     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4654   }
4655
4656   /* copy request gadgets to door backbuffer */
4657   BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4658
4659   OpenDoor(DOOR_OPEN_1);
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   if (!(req_state & REQ_STAY_OPEN))
4684   {
4685     CloseDoor(DOOR_CLOSE_1);
4686
4687     if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4688         (req_state & REQ_REOPEN))
4689       OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4690   }
4691
4692   RemapAllGadgets();
4693
4694   if (game_status == GAME_MODE_PLAYING)
4695   {
4696     SetPanelBackground();
4697     SetDrawBackgroundMask(REDRAW_DOOR_1);
4698   }
4699   else
4700   {
4701     SetDrawBackgroundMask(REDRAW_FIELD);
4702   }
4703
4704   /* continue network game after request */
4705   if (network.enabled &&
4706       game_status == GAME_MODE_PLAYING &&
4707       req_state & REQUEST_WAIT_FOR_INPUT)
4708     SendToServer_ContinuePlaying();
4709
4710   /* restore deactivated drawing when quick-loading level tape recording */
4711   if (tape.playing && tape.deactivate_display)
4712     TapeDeactivateDisplayOn();
4713
4714   return result;
4715 }
4716
4717 static boolean RequestEnvelope(char *text, unsigned int req_state)
4718 {
4719   int result;
4720
4721   if (game_status == GAME_MODE_PLAYING)
4722     BlitScreenToBitmap(backbuffer);
4723
4724   /* disable deactivated drawing when quick-loading level tape recording */
4725   if (tape.playing && tape.deactivate_display)
4726     TapeDeactivateDisplayOff(TRUE);
4727
4728   SetMouseCursor(CURSOR_DEFAULT);
4729
4730   /* pause network game while waiting for request to answer */
4731   if (network.enabled &&
4732       game_status == GAME_MODE_PLAYING &&
4733       req_state & REQUEST_WAIT_FOR_INPUT)
4734     SendToServer_PausePlaying();
4735
4736   /* simulate releasing mouse button over last gadget, if still pressed */
4737   if (button_status)
4738     HandleGadgets(-1, -1, 0);
4739
4740   UnmapAllGadgets();
4741
4742   // (replace with setting corresponding request background)
4743   // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4744   // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4745
4746   /* clear door drawing field */
4747   // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4748
4749   ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4750
4751   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4752   {
4753     if (game_status == GAME_MODE_PLAYING)
4754     {
4755       SetPanelBackground();
4756       SetDrawBackgroundMask(REDRAW_DOOR_1);
4757     }
4758     else
4759     {
4760       SetDrawBackgroundMask(REDRAW_FIELD);
4761     }
4762
4763     return FALSE;
4764   }
4765
4766   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4767
4768   // ---------- handle request buttons ----------
4769   result = RequestHandleEvents(req_state);
4770
4771   UnmapToolButtons();
4772
4773   ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4774
4775   RemapAllGadgets();
4776
4777   if (game_status == GAME_MODE_PLAYING)
4778   {
4779     SetPanelBackground();
4780     SetDrawBackgroundMask(REDRAW_DOOR_1);
4781   }
4782   else
4783   {
4784     SetDrawBackgroundMask(REDRAW_FIELD);
4785   }
4786
4787   /* continue network game after request */
4788   if (network.enabled &&
4789       game_status == GAME_MODE_PLAYING &&
4790       req_state & REQUEST_WAIT_FOR_INPUT)
4791     SendToServer_ContinuePlaying();
4792
4793   /* restore deactivated drawing when quick-loading level tape recording */
4794   if (tape.playing && tape.deactivate_display)
4795     TapeDeactivateDisplayOn();
4796
4797   return result;
4798 }
4799
4800 boolean Request(char *text, unsigned int req_state)
4801 {
4802   boolean overlay_active = GetOverlayActive();
4803   boolean result;
4804
4805   SetOverlayActive(FALSE);
4806
4807   if (global.use_envelope_request)
4808     result = RequestEnvelope(text, req_state);
4809   else
4810     result = RequestDoor(text, req_state);
4811
4812   SetOverlayActive(overlay_active);
4813
4814   return result;
4815 }
4816
4817 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4818 {
4819   const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4820   const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4821   int compare_result;
4822
4823   if (dpo1->sort_priority != dpo2->sort_priority)
4824     compare_result = dpo1->sort_priority - dpo2->sort_priority;
4825   else
4826     compare_result = dpo1->nr - dpo2->nr;
4827
4828   return compare_result;
4829 }
4830
4831 void InitGraphicCompatibilityInfo_Doors()
4832 {
4833   struct
4834   {
4835     int door_token;
4836     int part_1, part_8;
4837     struct DoorInfo *door;
4838   }
4839   doors[] =
4840   {
4841     { DOOR_1,   IMG_GFX_DOOR_1_PART_1,  IMG_GFX_DOOR_1_PART_8,  &door_1 },
4842     { DOOR_2,   IMG_GFX_DOOR_2_PART_1,  IMG_GFX_DOOR_2_PART_8,  &door_2 },
4843
4844     { -1,       -1,                     -1,                     NULL    }
4845   };
4846   struct Rect door_rect_list[] =
4847   {
4848     { DX, DY, DXSIZE, DYSIZE },
4849     { VX, VY, VXSIZE, VYSIZE }
4850   };
4851   int i, j;
4852
4853   for (i = 0; doors[i].door_token != -1; i++)
4854   {
4855     int door_token = doors[i].door_token;
4856     int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4857     int part_1 = doors[i].part_1;
4858     int part_8 = doors[i].part_8;
4859     int part_2 = part_1 + 1;
4860     int part_3 = part_1 + 2;
4861     struct DoorInfo *door = doors[i].door;
4862     struct Rect *door_rect = &door_rect_list[door_index];
4863     boolean door_gfx_redefined = FALSE;
4864
4865     /* check if any door part graphic definitions have been redefined */
4866
4867     for (j = 0; door_part_controls[j].door_token != -1; j++)
4868     {
4869       struct DoorPartControlInfo *dpc = &door_part_controls[j];
4870       struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4871
4872       if (dpc->door_token == door_token && fi->redefined)
4873         door_gfx_redefined = TRUE;
4874     }
4875
4876     /* check for old-style door graphic/animation modifications */
4877
4878     if (!door_gfx_redefined)
4879     {
4880       if (door->anim_mode & ANIM_STATIC_PANEL)
4881       {
4882         door->panel.step_xoffset = 0;
4883         door->panel.step_yoffset = 0;
4884       }
4885
4886       if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4887       {
4888         struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4889         struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4890         int num_door_steps, num_panel_steps;
4891
4892         /* remove door part graphics other than the two default wings */
4893
4894         for (j = 0; door_part_controls[j].door_token != -1; j++)
4895         {
4896           struct DoorPartControlInfo *dpc = &door_part_controls[j];
4897           struct GraphicInfo *g = &graphic_info[dpc->graphic];
4898
4899           if (dpc->graphic >= part_3 &&
4900               dpc->graphic <= part_8)
4901             g->bitmap = NULL;
4902         }
4903
4904         /* set graphics and screen positions of the default wings */
4905
4906         g_part_1->width  = door_rect->width;
4907         g_part_1->height = door_rect->height;
4908         g_part_2->width  = door_rect->width;
4909         g_part_2->height = door_rect->height;
4910         g_part_2->src_x = door_rect->width;
4911         g_part_2->src_y = g_part_1->src_y;
4912
4913         door->part_2.x = door->part_1.x;
4914         door->part_2.y = door->part_1.y;
4915
4916         if (door->width != -1)
4917         {
4918           g_part_1->width = door->width;
4919           g_part_2->width = door->width;
4920
4921           // special treatment for graphics and screen position of right wing
4922           g_part_2->src_x += door_rect->width - door->width;
4923           door->part_2.x  += door_rect->width - door->width;
4924         }
4925
4926         if (door->height != -1)
4927         {
4928           g_part_1->height = door->height;
4929           g_part_2->height = door->height;
4930
4931           // special treatment for graphics and screen position of bottom wing
4932           g_part_2->src_y += door_rect->height - door->height;
4933           door->part_2.y  += door_rect->height - door->height;
4934         }
4935
4936         /* set animation delays for the default wings and panels */
4937
4938         door->part_1.step_delay = door->step_delay;
4939         door->part_2.step_delay = door->step_delay;
4940         door->panel.step_delay  = door->step_delay;
4941
4942         /* set animation draw order for the default wings */
4943
4944         door->part_1.sort_priority = 2; /* draw left wing over ... */
4945         door->part_2.sort_priority = 1; /*          ... right wing */
4946
4947         /* set animation draw offset for the default wings */
4948
4949         if (door->anim_mode & ANIM_HORIZONTAL)
4950         {
4951           door->part_1.step_xoffset = door->step_offset;
4952           door->part_1.step_yoffset = 0;
4953           door->part_2.step_xoffset = door->step_offset * -1;
4954           door->part_2.step_yoffset = 0;
4955
4956           num_door_steps = g_part_1->width / door->step_offset;
4957         }
4958         else    // ANIM_VERTICAL
4959         {
4960           door->part_1.step_xoffset = 0;
4961           door->part_1.step_yoffset = door->step_offset;
4962           door->part_2.step_xoffset = 0;
4963           door->part_2.step_yoffset = door->step_offset * -1;
4964
4965           num_door_steps = g_part_1->height / door->step_offset;
4966         }
4967
4968         /* set animation draw offset for the default panels */
4969
4970         if (door->step_offset > 1)
4971         {
4972           num_panel_steps = 2 * door_rect->height / door->step_offset;
4973           door->panel.start_step = num_panel_steps - num_door_steps;
4974           door->panel.start_step_closing = door->panel.start_step;
4975         }
4976         else
4977         {
4978           num_panel_steps = door_rect->height / door->step_offset;
4979           door->panel.start_step = num_panel_steps - num_door_steps / 2;
4980           door->panel.start_step_closing = door->panel.start_step;
4981           door->panel.step_delay *= 2;
4982         }
4983       }
4984     }
4985   }
4986 }
4987
4988 void InitDoors()
4989 {
4990   int i;
4991
4992   for (i = 0; door_part_controls[i].door_token != -1; i++)
4993   {
4994     struct DoorPartControlInfo *dpc = &door_part_controls[i];
4995     struct DoorPartOrderInfo *dpo = &door_part_order[i];
4996
4997     /* initialize "start_step_opening" and "start_step_closing", if needed */
4998     if (dpc->pos->start_step_opening == 0 &&
4999         dpc->pos->start_step_closing == 0)
5000     {
5001       // dpc->pos->start_step_opening = dpc->pos->start_step;
5002       dpc->pos->start_step_closing = dpc->pos->start_step;
5003     }
5004
5005     /* fill structure for door part draw order (sorted below) */
5006     dpo->nr = i;
5007     dpo->sort_priority = dpc->pos->sort_priority;
5008   }
5009
5010   /* sort door part controls according to sort_priority and graphic number */
5011   qsort(door_part_order, MAX_DOOR_PARTS,
5012         sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5013 }
5014
5015 unsigned int OpenDoor(unsigned int door_state)
5016 {
5017   if (door_state & DOOR_COPY_BACK)
5018   {
5019     if (door_state & DOOR_OPEN_1)
5020       BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5021                  1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5022
5023     if (door_state & DOOR_OPEN_2)
5024       BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5025                  1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5026
5027     door_state &= ~DOOR_COPY_BACK;
5028   }
5029
5030   return MoveDoor(door_state);
5031 }
5032
5033 unsigned int CloseDoor(unsigned int door_state)
5034 {
5035   unsigned int old_door_state = GetDoorState();
5036
5037   if (!(door_state & DOOR_NO_COPY_BACK))
5038   {
5039     if (old_door_state & DOOR_OPEN_1)
5040       BlitBitmap(backbuffer, bitmap_db_door_1,
5041                  DX, DY, DXSIZE, DYSIZE, 0, 0);
5042
5043     if (old_door_state & DOOR_OPEN_2)
5044       BlitBitmap(backbuffer, bitmap_db_door_2,
5045                  VX, VY, VXSIZE, VYSIZE, 0, 0);
5046
5047     door_state &= ~DOOR_NO_COPY_BACK;
5048   }
5049
5050   return MoveDoor(door_state);
5051 }
5052
5053 unsigned int GetDoorState()
5054 {
5055   return MoveDoor(DOOR_GET_STATE);
5056 }
5057
5058 unsigned int SetDoorState(unsigned int door_state)
5059 {
5060   return MoveDoor(door_state | DOOR_SET_STATE);
5061 }
5062
5063 int euclid(int a, int b)
5064 {
5065   return (b ? euclid(b, a % b) : a);
5066 }
5067
5068 unsigned int MoveDoor(unsigned int door_state)
5069 {
5070   struct Rect door_rect_list[] =
5071   {
5072     { DX, DY, DXSIZE, DYSIZE },
5073     { VX, VY, VXSIZE, VYSIZE }
5074   };
5075   static int door1 = DOOR_CLOSE_1;
5076   static int door2 = DOOR_CLOSE_2;
5077   unsigned int door_delay = 0;
5078   unsigned int door_delay_value;
5079   int i;
5080
5081   if (door_state == DOOR_GET_STATE)
5082     return (door1 | door2);
5083
5084   if (door_state & DOOR_SET_STATE)
5085   {
5086     if (door_state & DOOR_ACTION_1)
5087       door1 = door_state & DOOR_ACTION_1;
5088     if (door_state & DOOR_ACTION_2)
5089       door2 = door_state & DOOR_ACTION_2;
5090
5091     return (door1 | door2);
5092   }
5093
5094   if (!(door_state & DOOR_FORCE_REDRAW))
5095   {
5096     if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5097       door_state &= ~DOOR_OPEN_1;
5098     else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5099       door_state &= ~DOOR_CLOSE_1;
5100     if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5101       door_state &= ~DOOR_OPEN_2;
5102     else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5103       door_state &= ~DOOR_CLOSE_2;
5104   }
5105
5106   if (global.autoplay_leveldir)
5107   {
5108     door_state |= DOOR_NO_DELAY;
5109     door_state &= ~DOOR_CLOSE_ALL;
5110   }
5111
5112   if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5113     door_state |= DOOR_NO_DELAY;
5114
5115   if (door_state & DOOR_ACTION)
5116   {
5117     boolean door_panel_drawn[NUM_DOORS];
5118     boolean panel_has_doors[NUM_DOORS];
5119     boolean door_part_skip[MAX_DOOR_PARTS];
5120     boolean door_part_done[MAX_DOOR_PARTS];
5121     boolean door_part_done_all;
5122     int num_steps[MAX_DOOR_PARTS];
5123     int max_move_delay = 0;     // delay for complete animations of all doors
5124     int max_step_delay = 0;     // delay (ms) between two animation frames
5125     int num_move_steps = 0;     // number of animation steps for all doors
5126     int max_move_delay_doors_only = 0;  // delay for doors only (no panel)
5127     int num_move_steps_doors_only = 0;  // steps for doors only (no panel)
5128     int current_move_delay = 0;
5129     int start = 0;
5130     int k;
5131
5132     for (i = 0; i < NUM_DOORS; i++)
5133       panel_has_doors[i] = FALSE;
5134
5135     for (i = 0; i < MAX_DOOR_PARTS; i++)
5136     {
5137       struct DoorPartControlInfo *dpc = &door_part_controls[i];
5138       struct GraphicInfo *g = &graphic_info[dpc->graphic];
5139       int door_token = dpc->door_token;
5140
5141       door_part_done[i] = FALSE;
5142       door_part_skip[i] = (!(door_state & door_token) ||
5143                            !g->bitmap);
5144     }
5145
5146     for (i = 0; i < MAX_DOOR_PARTS; i++)
5147     {
5148       int nr = door_part_order[i].nr;
5149       struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5150       struct DoorPartPosInfo *pos = dpc->pos;
5151       struct GraphicInfo *g = &graphic_info[dpc->graphic];
5152       int door_token = dpc->door_token;
5153       int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5154       boolean is_panel = DOOR_PART_IS_PANEL(nr);
5155       int step_xoffset = ABS(pos->step_xoffset);
5156       int step_yoffset = ABS(pos->step_yoffset);
5157       int step_delay = pos->step_delay;
5158       int current_door_state = door_state & door_token;
5159       boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
5160       boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5161       boolean part_opening = (is_panel ? door_closing : door_opening);
5162       int start_step = (part_opening ? pos->start_step_opening :
5163                         pos->start_step_closing);
5164       float move_xsize = (step_xoffset ? g->width  : 0);
5165       float move_ysize = (step_yoffset ? g->height : 0);
5166       int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5167       int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5168       int move_steps = (move_xsteps && move_ysteps ?
5169                         MIN(move_xsteps, move_ysteps) :
5170                         move_xsteps ? move_xsteps : move_ysteps) - start_step;
5171       int move_delay = move_steps * step_delay;
5172
5173       if (door_part_skip[nr])
5174         continue;
5175
5176       max_move_delay = MAX(max_move_delay, move_delay);
5177       max_step_delay = (max_step_delay == 0 ? step_delay :
5178                         euclid(max_step_delay, step_delay));
5179       num_steps[nr] = move_steps;
5180
5181       if (!is_panel)
5182       {
5183         max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5184
5185         panel_has_doors[door_index] = TRUE;
5186       }
5187     }
5188
5189     max_step_delay = MAX(1, max_step_delay);    // prevent division by zero
5190
5191     num_move_steps = max_move_delay / max_step_delay;
5192     num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5193
5194     door_delay_value = max_step_delay;
5195
5196     if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5197     {
5198       start = num_move_steps - 1;
5199     }
5200     else
5201     {
5202       /* opening door sound has priority over simultaneously closing door */
5203       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5204       {
5205         PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5206
5207         if (door_state & DOOR_OPEN_1)
5208           PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5209         if (door_state & DOOR_OPEN_2)
5210           PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5211       }
5212       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5213       {
5214         PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5215
5216         if (door_state & DOOR_CLOSE_1)
5217           PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5218         if (door_state & DOOR_CLOSE_2)
5219           PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5220       }
5221     }
5222
5223     for (k = start; k < num_move_steps; k++)
5224     {
5225       int last_frame = num_move_steps - 1;      // last frame of this "for" loop
5226
5227       door_part_done_all = TRUE;
5228
5229       for (i = 0; i < NUM_DOORS; i++)
5230         door_panel_drawn[i] = FALSE;
5231
5232       for (i = 0; i < MAX_DOOR_PARTS; i++)
5233       {
5234         int nr = door_part_order[i].nr;
5235         struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5236         struct DoorPartPosInfo *pos = dpc->pos;
5237         struct GraphicInfo *g = &graphic_info[dpc->graphic];
5238         int door_token = dpc->door_token;
5239         int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5240         boolean is_panel = DOOR_PART_IS_PANEL(nr);
5241         boolean is_panel_and_door_has_closed = FALSE;
5242         struct Rect *door_rect = &door_rect_list[door_index];
5243         Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5244                                   bitmap_db_door_2);
5245         Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5246         int current_door_state = door_state & door_token;
5247         boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
5248         boolean door_closing = !door_opening;
5249         boolean part_opening = (is_panel ? door_closing : door_opening);
5250         boolean part_closing = !part_opening;
5251         int start_step = (part_opening ? pos->start_step_opening :
5252                           pos->start_step_closing);
5253         int step_delay = pos->step_delay;
5254         int step_factor = step_delay / max_step_delay;
5255         int k1 = (step_factor ? k / step_factor + 1 : k);
5256         int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5257         int kk = MAX(0, k2);
5258         int g_src_x = 0;
5259         int g_src_y = 0;
5260         int src_x, src_y, src_xx, src_yy;
5261         int dst_x, dst_y, dst_xx, dst_yy;
5262         int width, height;
5263
5264         if (door_part_skip[nr])
5265           continue;
5266
5267         if (!(door_state & door_token))
5268           continue;
5269
5270         if (!g->bitmap)
5271           continue;
5272
5273         if (!is_panel)
5274         {
5275           int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5276           int kk_door = MAX(0, k2_door);
5277           int sync_frame = kk_door * door_delay_value;
5278           int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5279
5280           getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5281                                 &g_src_x, &g_src_y);
5282         }
5283
5284         // draw door panel
5285
5286         if (!door_panel_drawn[door_index])
5287         {
5288           ClearRectangle(drawto, door_rect->x, door_rect->y,
5289                          door_rect->width, door_rect->height);
5290
5291           door_panel_drawn[door_index] = TRUE;
5292         }
5293
5294         // draw opening or closing door parts
5295
5296         if (pos->step_xoffset < 0)      // door part on right side
5297         {
5298           src_xx = 0;
5299           dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5300           width = g->width;
5301
5302           if (dst_xx + width > door_rect->width)
5303             width = door_rect->width - dst_xx;
5304         }
5305         else                            // door part on left side
5306         {
5307           src_xx = 0;
5308           dst_xx = pos->x - kk * pos->step_xoffset;
5309
5310           if (dst_xx < 0)
5311           {
5312             src_xx = ABS(dst_xx);
5313             dst_xx = 0;
5314           }
5315
5316           width = g->width - src_xx;
5317
5318           if (width > door_rect->width)
5319             width = door_rect->width;
5320
5321           // printf("::: k == %d [%d] \n", k, start_step);
5322         }
5323
5324         if (pos->step_yoffset < 0)      // door part on bottom side
5325         {
5326           src_yy = 0;
5327           dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5328           height = g->height;
5329
5330           if (dst_yy + height > door_rect->height)
5331             height = door_rect->height - dst_yy;
5332         }
5333         else                            // door part on top side
5334         {
5335           src_yy = 0;
5336           dst_yy = pos->y - kk * pos->step_yoffset;
5337
5338           if (dst_yy < 0)
5339           {
5340             src_yy = ABS(dst_yy);
5341             dst_yy = 0;
5342           }
5343
5344           height = g->height - src_yy;
5345         }
5346
5347         src_x = g_src_x + src_xx;
5348         src_y = g_src_y + src_yy;
5349
5350         dst_x = door_rect->x + dst_xx;
5351         dst_y = door_rect->y + dst_yy;
5352
5353         is_panel_and_door_has_closed =
5354           (is_panel &&
5355            door_closing &&
5356            panel_has_doors[door_index] &&
5357            k >= num_move_steps_doors_only - 1);
5358
5359         if (width  >= 0 && width  <= g->width &&
5360             height >= 0 && height <= g->height &&
5361             !is_panel_and_door_has_closed)
5362         {
5363           if (is_panel || !pos->draw_masked)
5364             BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5365                        dst_x, dst_y);
5366           else
5367             BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5368                              dst_x, dst_y);
5369         }
5370
5371         redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5372
5373         if ((part_opening && (width < 0         || height < 0)) ||
5374             (part_closing && (width >= g->width && height >= g->height)))
5375           door_part_done[nr] = TRUE;
5376
5377         // continue door part animations, but not panel after door has closed
5378         if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5379           door_part_done_all = FALSE;
5380       }
5381
5382       if (!(door_state & DOOR_NO_DELAY))
5383       {
5384         BackToFront();
5385
5386         SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5387
5388         current_move_delay += max_step_delay;
5389
5390         /* prevent OS (Windows) from complaining about program not responding */
5391         CheckQuitEvent();
5392       }
5393
5394       if (door_part_done_all)
5395         break;
5396     }
5397
5398     if (!(door_state & DOOR_NO_DELAY))
5399     {
5400       /* wait for specified door action post delay */
5401       if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5402         door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5403       else if (door_state & DOOR_ACTION_1)
5404         door_delay_value = door_1.post_delay;
5405       else if (door_state & DOOR_ACTION_2)
5406         door_delay_value = door_2.post_delay;
5407
5408       while (!DelayReached(&door_delay, door_delay_value))
5409         BackToFront();
5410     }
5411   }
5412
5413   if (door_state & DOOR_ACTION_1)
5414     door1 = door_state & DOOR_ACTION_1;
5415   if (door_state & DOOR_ACTION_2)
5416     door2 = door_state & DOOR_ACTION_2;
5417
5418   // draw masked border over door area
5419   DrawMaskedBorder(REDRAW_DOOR_1);
5420   DrawMaskedBorder(REDRAW_DOOR_2);
5421
5422   return (door1 | door2);
5423 }
5424
5425 static boolean useSpecialEditorDoor()
5426 {
5427   int graphic = IMG_GLOBAL_BORDER_EDITOR;
5428   boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5429
5430   // do not draw special editor door if editor border defined or redefined
5431   if (graphic_info[graphic].bitmap != NULL || redefined)
5432     return FALSE;
5433
5434   // do not draw special editor door if global border defined to be empty
5435   if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5436     return FALSE;
5437
5438   // do not draw special editor door if viewport definitions do not match
5439   if (EX != VX ||
5440       EY >= VY ||
5441       EXSIZE != VXSIZE ||
5442       EY + EYSIZE != VY + VYSIZE)
5443     return FALSE;
5444
5445   return TRUE;
5446 }
5447
5448 void DrawSpecialEditorDoor()
5449 {
5450   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5451   int top_border_width = gfx1->width;
5452   int top_border_height = gfx1->height;
5453   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5454   int ex = EX - outer_border;
5455   int ey = EY - outer_border;
5456   int vy = VY - outer_border;
5457   int exsize = EXSIZE + 2 * outer_border;
5458
5459   if (!useSpecialEditorDoor())
5460     return;
5461
5462   /* draw bigger level editor toolbox window */
5463   BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5464              top_border_width, top_border_height, ex, ey - top_border_height);
5465   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5466              exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5467
5468   redraw_mask |= REDRAW_ALL;
5469 }
5470
5471 void UndrawSpecialEditorDoor()
5472 {
5473   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5474   int top_border_width = gfx1->width;
5475   int top_border_height = gfx1->height;
5476   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5477   int ex = EX - outer_border;
5478   int ey = EY - outer_border;
5479   int ey_top = ey - top_border_height;
5480   int exsize = EXSIZE + 2 * outer_border;
5481   int eysize = EYSIZE + 2 * outer_border;
5482
5483   if (!useSpecialEditorDoor())
5484     return;
5485
5486   /* draw normal tape recorder window */
5487   if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5488   {
5489     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5490                ex, ey_top, top_border_width, top_border_height,
5491                ex, ey_top);
5492     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5493                ex, ey, exsize, eysize, ex, ey);
5494   }
5495   else
5496   {
5497     // if screen background is set to "[NONE]", clear editor toolbox window
5498     ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5499     ClearRectangle(drawto, ex, ey, exsize, eysize);
5500   }
5501
5502   redraw_mask |= REDRAW_ALL;
5503 }
5504
5505
5506 /* ---------- new tool button stuff ---------------------------------------- */
5507
5508 static struct
5509 {
5510   int graphic;
5511   struct TextPosInfo *pos;
5512   int gadget_id;
5513   char *infotext;
5514 } toolbutton_info[NUM_TOOL_BUTTONS] =
5515 {
5516   {
5517     IMG_GFX_REQUEST_BUTTON_YES,         &request.button.yes,
5518     TOOL_CTRL_ID_YES,                   "yes"
5519   },
5520   {
5521     IMG_GFX_REQUEST_BUTTON_NO,          &request.button.no,
5522     TOOL_CTRL_ID_NO,                    "no"
5523   },
5524   {
5525     IMG_GFX_REQUEST_BUTTON_CONFIRM,     &request.button.confirm,
5526     TOOL_CTRL_ID_CONFIRM,               "confirm"
5527   },
5528   {
5529     IMG_GFX_REQUEST_BUTTON_PLAYER_1,    &request.button.player_1,
5530     TOOL_CTRL_ID_PLAYER_1,              "player 1"
5531   },
5532   {
5533     IMG_GFX_REQUEST_BUTTON_PLAYER_2,    &request.button.player_2,
5534     TOOL_CTRL_ID_PLAYER_2,              "player 2"
5535   },
5536   {
5537     IMG_GFX_REQUEST_BUTTON_PLAYER_3,    &request.button.player_3,
5538     TOOL_CTRL_ID_PLAYER_3,              "player 3"
5539   },
5540   {
5541     IMG_GFX_REQUEST_BUTTON_PLAYER_4,    &request.button.player_4,
5542     TOOL_CTRL_ID_PLAYER_4,              "player 4"
5543   }
5544 };
5545
5546 void CreateToolButtons()
5547 {
5548   int i;
5549
5550   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5551   {
5552     int graphic = toolbutton_info[i].graphic;
5553     struct GraphicInfo *gfx = &graphic_info[graphic];
5554     struct TextPosInfo *pos = toolbutton_info[i].pos;
5555     struct GadgetInfo *gi;
5556     Bitmap *deco_bitmap = None;
5557     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5558     unsigned int event_mask = GD_EVENT_RELEASED;
5559     int dx = DX;
5560     int dy = DY;
5561     int gd_x = gfx->src_x;
5562     int gd_y = gfx->src_y;
5563     int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5564     int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5565     int x = pos->x;
5566     int y = pos->y;
5567     int id = i;
5568
5569     if (global.use_envelope_request)
5570     {
5571       setRequestPosition(&dx, &dy, TRUE);
5572
5573       // check if request buttons are outside of envelope and fix, if needed
5574       if (x < 0 || x + gfx->width  > request.width ||
5575           y < 0 || y + gfx->height > request.height)
5576       {
5577         if (id == TOOL_CTRL_ID_YES)
5578         {
5579           x = 0;
5580           y = request.height - 2 * request.border_size - gfx->height;
5581         }
5582         else if (id == TOOL_CTRL_ID_NO)
5583         {
5584           x = request.width  - 2 * request.border_size - gfx->width;
5585           y = request.height - 2 * request.border_size - gfx->height;
5586         }
5587         else if (id == TOOL_CTRL_ID_CONFIRM)
5588         {
5589           x = (request.width - 2 * request.border_size - gfx->width) / 2;
5590           y = request.height - 2 * request.border_size - gfx->height;
5591         }
5592         else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5593         {
5594           int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5595
5596           x = (request.width - 2 * request.border_size - gfx->width) / 2;
5597           y = request.height - 2 * request.border_size - gfx->height * 2;
5598
5599           x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5600           y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5601         }
5602       }
5603     }
5604
5605     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5606     {
5607       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5608
5609       getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5610                             pos->size, &deco_bitmap, &deco_x, &deco_y);
5611       deco_xpos = (gfx->width  - pos->size) / 2;
5612       deco_ypos = (gfx->height - pos->size) / 2;
5613     }
5614
5615     gi = CreateGadget(GDI_CUSTOM_ID, id,
5616                       GDI_IMAGE_ID, graphic,
5617                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
5618                       GDI_X, dx + x,
5619                       GDI_Y, dy + y,
5620                       GDI_WIDTH, gfx->width,
5621                       GDI_HEIGHT, gfx->height,
5622                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5623                       GDI_STATE, GD_BUTTON_UNPRESSED,
5624                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5625                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5626                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5627                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5628                       GDI_DECORATION_SIZE, pos->size, pos->size,
5629                       GDI_DECORATION_SHIFTING, 1, 1,
5630                       GDI_DIRECT_DRAW, FALSE,
5631                       GDI_EVENT_MASK, event_mask,
5632                       GDI_CALLBACK_ACTION, HandleToolButtons,
5633                       GDI_END);
5634
5635     if (gi == NULL)
5636       Error(ERR_EXIT, "cannot create gadget");
5637
5638     tool_gadget[id] = gi;
5639   }
5640 }
5641
5642 void FreeToolButtons()
5643 {
5644   int i;
5645
5646   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5647     FreeGadget(tool_gadget[i]);
5648 }
5649
5650 static void UnmapToolButtons()
5651 {
5652   int i;
5653
5654   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5655     UnmapGadget(tool_gadget[i]);
5656 }
5657
5658 static void HandleToolButtons(struct GadgetInfo *gi)
5659 {
5660   request_gadget_id = gi->custom_id;
5661 }
5662
5663 static struct Mapping_EM_to_RND_object
5664 {
5665   int element_em;
5666   boolean is_rnd_to_em_mapping;         /* unique mapping EM <-> RND */
5667   boolean is_backside;                  /* backside of moving element */
5668
5669   int element_rnd;
5670   int action;
5671   int direction;
5672 }
5673 em_object_mapping_list[] =
5674 {
5675   {
5676     Xblank,                             TRUE,   FALSE,
5677     EL_EMPTY,                           -1, -1
5678   },
5679   {
5680     Yacid_splash_eB,                    FALSE,  FALSE,
5681     EL_ACID_SPLASH_RIGHT,               -1, -1
5682   },
5683   {
5684     Yacid_splash_wB,                    FALSE,  FALSE,
5685     EL_ACID_SPLASH_LEFT,                -1, -1
5686   },
5687
5688 #ifdef EM_ENGINE_BAD_ROLL
5689   {
5690     Xstone_force_e,                     FALSE,  FALSE,
5691     EL_ROCK,                            -1, MV_BIT_RIGHT
5692   },
5693   {
5694     Xstone_force_w,                     FALSE,  FALSE,
5695     EL_ROCK,                            -1, MV_BIT_LEFT
5696   },
5697   {
5698     Xnut_force_e,                       FALSE,  FALSE,
5699     EL_NUT,                             -1, MV_BIT_RIGHT
5700   },
5701   {
5702     Xnut_force_w,                       FALSE,  FALSE,
5703     EL_NUT,                             -1, MV_BIT_LEFT
5704   },
5705   {
5706     Xspring_force_e,                    FALSE,  FALSE,
5707     EL_SPRING,                          -1, MV_BIT_RIGHT
5708   },
5709   {
5710     Xspring_force_w,                    FALSE,  FALSE,
5711     EL_SPRING,                          -1, MV_BIT_LEFT
5712   },
5713   {
5714     Xemerald_force_e,                   FALSE,  FALSE,
5715     EL_EMERALD,                         -1, MV_BIT_RIGHT
5716   },
5717   {
5718     Xemerald_force_w,                   FALSE,  FALSE,
5719     EL_EMERALD,                         -1, MV_BIT_LEFT
5720   },
5721   {
5722     Xdiamond_force_e,                   FALSE,  FALSE,
5723     EL_DIAMOND,                         -1, MV_BIT_RIGHT
5724   },
5725   {
5726     Xdiamond_force_w,                   FALSE,  FALSE,
5727     EL_DIAMOND,                         -1, MV_BIT_LEFT
5728   },
5729   {
5730     Xbomb_force_e,                      FALSE,  FALSE,
5731     EL_BOMB,                            -1, MV_BIT_RIGHT
5732   },
5733   {
5734     Xbomb_force_w,                      FALSE,  FALSE,
5735     EL_BOMB,                            -1, MV_BIT_LEFT
5736   },
5737 #endif  /* EM_ENGINE_BAD_ROLL */
5738
5739   {
5740     Xstone,                             TRUE,   FALSE,
5741     EL_ROCK,                            -1, -1
5742   },
5743   {
5744     Xstone_pause,                       FALSE,  FALSE,
5745     EL_ROCK,                            -1, -1
5746   },
5747   {
5748     Xstone_fall,                        FALSE,  FALSE,
5749     EL_ROCK,                            -1, -1
5750   },
5751   {
5752     Ystone_s,                           FALSE,  FALSE,
5753     EL_ROCK,                            ACTION_FALLING, -1
5754   },
5755   {
5756     Ystone_sB,                          FALSE,  TRUE,
5757     EL_ROCK,                            ACTION_FALLING, -1
5758   },
5759   {
5760     Ystone_e,                           FALSE,  FALSE,
5761     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
5762   },
5763   {
5764     Ystone_eB,                          FALSE,  TRUE,
5765     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
5766   },
5767   {
5768     Ystone_w,                           FALSE,  FALSE,
5769     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
5770   },
5771   {
5772     Ystone_wB,                          FALSE,  TRUE,
5773     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
5774   },
5775   {
5776     Xnut,                               TRUE,   FALSE,
5777     EL_NUT,                             -1, -1
5778   },
5779   {
5780     Xnut_pause,                         FALSE,  FALSE,
5781     EL_NUT,                             -1, -1
5782   },
5783   {
5784     Xnut_fall,                          FALSE,  FALSE,
5785     EL_NUT,                             -1, -1
5786   },
5787   {
5788     Ynut_s,                             FALSE,  FALSE,
5789     EL_NUT,                             ACTION_FALLING, -1
5790   },
5791   {
5792     Ynut_sB,                            FALSE,  TRUE,
5793     EL_NUT,                             ACTION_FALLING, -1
5794   },
5795   {
5796     Ynut_e,                             FALSE,  FALSE,
5797     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
5798   },
5799   {
5800     Ynut_eB,                            FALSE,  TRUE,
5801     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
5802   },
5803   {
5804     Ynut_w,                             FALSE,  FALSE,
5805     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
5806   },
5807   {
5808     Ynut_wB,                            FALSE,  TRUE,
5809     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
5810   },
5811   {
5812     Xbug_n,                             TRUE,   FALSE,
5813     EL_BUG_UP,                          -1, -1
5814   },
5815   {
5816     Xbug_e,                             TRUE,   FALSE,
5817     EL_BUG_RIGHT,                       -1, -1
5818   },
5819   {
5820     Xbug_s,                             TRUE,   FALSE,
5821     EL_BUG_DOWN,                        -1, -1
5822   },
5823   {
5824     Xbug_w,                             TRUE,   FALSE,
5825     EL_BUG_LEFT,                        -1, -1
5826   },
5827   {
5828     Xbug_gon,                           FALSE,  FALSE,
5829     EL_BUG_UP,                          -1, -1
5830   },
5831   {
5832     Xbug_goe,                           FALSE,  FALSE,
5833     EL_BUG_RIGHT,                       -1, -1
5834   },
5835   {
5836     Xbug_gos,                           FALSE,  FALSE,
5837     EL_BUG_DOWN,                        -1, -1
5838   },
5839   {
5840     Xbug_gow,                           FALSE,  FALSE,
5841     EL_BUG_LEFT,                        -1, -1
5842   },
5843   {
5844     Ybug_n,                             FALSE,  FALSE,
5845     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
5846   },
5847   {
5848     Ybug_nB,                            FALSE,  TRUE,
5849     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
5850   },
5851   {
5852     Ybug_e,                             FALSE,  FALSE,
5853     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
5854   },
5855   {
5856     Ybug_eB,                            FALSE,  TRUE,
5857     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
5858   },
5859   {
5860     Ybug_s,                             FALSE,  FALSE,
5861     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
5862   },
5863   {
5864     Ybug_sB,                            FALSE,  TRUE,
5865     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
5866   },
5867   {
5868     Ybug_w,                             FALSE,  FALSE,
5869     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
5870   },
5871   {
5872     Ybug_wB,                            FALSE,  TRUE,
5873     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
5874   },
5875   {
5876     Ybug_w_n,                           FALSE,  FALSE,
5877     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5878   },
5879   {
5880     Ybug_n_e,                           FALSE,  FALSE,
5881     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5882   },
5883   {
5884     Ybug_e_s,                           FALSE,  FALSE,
5885     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5886   },
5887   {
5888     Ybug_s_w,                           FALSE,  FALSE,
5889     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5890   },
5891   {
5892     Ybug_e_n,                           FALSE,  FALSE,
5893     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5894   },
5895   {
5896     Ybug_s_e,                           FALSE,  FALSE,
5897     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5898   },
5899   {
5900     Ybug_w_s,                           FALSE,  FALSE,
5901     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5902   },
5903   {
5904     Ybug_n_w,                           FALSE,  FALSE,
5905     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5906   },
5907   {
5908     Ybug_stone,                         FALSE,  FALSE,
5909     EL_BUG,                             ACTION_SMASHED_BY_ROCK, -1
5910   },
5911   {
5912     Ybug_spring,                        FALSE,  FALSE,
5913     EL_BUG,                             ACTION_SMASHED_BY_SPRING, -1
5914   },
5915   {
5916     Xtank_n,                            TRUE,   FALSE,
5917     EL_SPACESHIP_UP,                    -1, -1
5918   },
5919   {
5920     Xtank_e,                            TRUE,   FALSE,
5921     EL_SPACESHIP_RIGHT,                 -1, -1
5922   },
5923   {
5924     Xtank_s,                            TRUE,   FALSE,
5925     EL_SPACESHIP_DOWN,                  -1, -1
5926   },
5927   {
5928     Xtank_w,                            TRUE,   FALSE,
5929     EL_SPACESHIP_LEFT,                  -1, -1
5930   },
5931   {
5932     Xtank_gon,                          FALSE,  FALSE,
5933     EL_SPACESHIP_UP,                    -1, -1
5934   },
5935   {
5936     Xtank_goe,                          FALSE,  FALSE,
5937     EL_SPACESHIP_RIGHT,                 -1, -1
5938   },
5939   {
5940     Xtank_gos,                          FALSE,  FALSE,
5941     EL_SPACESHIP_DOWN,                  -1, -1
5942   },
5943   {
5944     Xtank_gow,                          FALSE,  FALSE,
5945     EL_SPACESHIP_LEFT,                  -1, -1
5946   },
5947   {
5948     Ytank_n,                            FALSE,  FALSE,
5949     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
5950   },
5951   {
5952     Ytank_nB,                           FALSE,  TRUE,
5953     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
5954   },
5955   {
5956     Ytank_e,                            FALSE,  FALSE,
5957     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
5958   },
5959   {
5960     Ytank_eB,                           FALSE,  TRUE,
5961     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
5962   },
5963   {
5964     Ytank_s,                            FALSE,  FALSE,
5965     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
5966   },
5967   {
5968     Ytank_sB,                           FALSE,  TRUE,
5969     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
5970   },
5971   {
5972     Ytank_w,                            FALSE,  FALSE,
5973     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
5974   },
5975   {
5976     Ytank_wB,                           FALSE,  TRUE,
5977     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
5978   },
5979   {
5980     Ytank_w_n,                          FALSE,  FALSE,
5981     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5982   },
5983   {
5984     Ytank_n_e,                          FALSE,  FALSE,
5985     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5986   },
5987   {
5988     Ytank_e_s,                          FALSE,  FALSE,
5989     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5990   },
5991   {
5992     Ytank_s_w,                          FALSE,  FALSE,
5993     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5994   },
5995   {
5996     Ytank_e_n,                          FALSE,  FALSE,
5997     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5998   },
5999   {
6000     Ytank_s_e,                          FALSE,  FALSE,
6001     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6002   },
6003   {
6004     Ytank_w_s,                          FALSE,  FALSE,
6005     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6006   },
6007   {
6008     Ytank_n_w,                          FALSE,  FALSE,
6009     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6010   },
6011   {
6012     Ytank_stone,                        FALSE,  FALSE,
6013     EL_SPACESHIP,                       ACTION_SMASHED_BY_ROCK, -1
6014   },
6015   {
6016     Ytank_spring,                       FALSE,  FALSE,
6017     EL_SPACESHIP,                       ACTION_SMASHED_BY_SPRING, -1
6018   },
6019   {
6020     Xandroid,                           TRUE,   FALSE,
6021     EL_EMC_ANDROID,                     ACTION_ACTIVE, -1
6022   },
6023   {
6024     Xandroid_1_n,                       FALSE,  FALSE,
6025     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
6026   },
6027   {
6028     Xandroid_2_n,                       FALSE,  FALSE,
6029     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
6030   },
6031   {
6032     Xandroid_1_e,                       FALSE,  FALSE,
6033     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
6034   },
6035   {
6036     Xandroid_2_e,                       FALSE,  FALSE,
6037     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
6038   },
6039   {
6040     Xandroid_1_w,                       FALSE,  FALSE,
6041     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
6042   },
6043   {
6044     Xandroid_2_w,                       FALSE,  FALSE,
6045     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
6046   },
6047   {
6048     Xandroid_1_s,                       FALSE,  FALSE,
6049     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
6050   },
6051   {
6052     Xandroid_2_s,                       FALSE,  FALSE,
6053     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
6054   },
6055   {
6056     Yandroid_n,                         FALSE,  FALSE,
6057     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
6058   },
6059   {
6060     Yandroid_nB,                        FALSE,  TRUE,
6061     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
6062   },
6063   {
6064     Yandroid_ne,                        FALSE,  FALSE,
6065     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPRIGHT
6066   },
6067   {
6068     Yandroid_neB,                       FALSE,  TRUE,
6069     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPRIGHT
6070   },
6071   {
6072     Yandroid_e,                         FALSE,  FALSE,
6073     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
6074   },
6075   {
6076     Yandroid_eB,                        FALSE,  TRUE,
6077     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
6078   },
6079   {
6080     Yandroid_se,                        FALSE,  FALSE,
6081     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNRIGHT
6082   },
6083   {
6084     Yandroid_seB,                       FALSE,  TRUE,
6085     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6086   },
6087   {
6088     Yandroid_s,                         FALSE,  FALSE,
6089     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
6090   },
6091   {
6092     Yandroid_sB,                        FALSE,  TRUE,
6093     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
6094   },
6095   {
6096     Yandroid_sw,                        FALSE,  FALSE,
6097     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNLEFT
6098   },
6099   {
6100     Yandroid_swB,                       FALSE,  TRUE,
6101     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNLEFT
6102   },
6103   {
6104     Yandroid_w,                         FALSE,  FALSE,
6105     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
6106   },
6107   {
6108     Yandroid_wB,                        FALSE,  TRUE,
6109     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
6110   },
6111   {
6112     Yandroid_nw,                        FALSE,  FALSE,
6113     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPLEFT
6114   },
6115   {
6116     Yandroid_nwB,                       FALSE,  TRUE,
6117     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPLEFT
6118   },
6119   {
6120     Xspring,                            TRUE,   FALSE,
6121     EL_SPRING,                          -1, -1
6122   },
6123   {
6124     Xspring_pause,                      FALSE,  FALSE,
6125     EL_SPRING,                          -1, -1
6126   },
6127   {
6128     Xspring_e,                          FALSE,  FALSE,
6129     EL_SPRING,                          -1, -1
6130   },
6131   {
6132     Xspring_w,                          FALSE,  FALSE,
6133     EL_SPRING,                          -1, -1
6134   },
6135   {
6136     Xspring_fall,                       FALSE,  FALSE,
6137     EL_SPRING,                          -1, -1
6138   },
6139   {
6140     Yspring_s,                          FALSE,  FALSE,
6141     EL_SPRING,                          ACTION_FALLING, -1
6142   },
6143   {
6144     Yspring_sB,                         FALSE,  TRUE,
6145     EL_SPRING,                          ACTION_FALLING, -1
6146   },
6147   {
6148     Yspring_e,                          FALSE,  FALSE,
6149     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
6150   },
6151   {
6152     Yspring_eB,                         FALSE,  TRUE,
6153     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
6154   },
6155   {
6156     Yspring_w,                          FALSE,  FALSE,
6157     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
6158   },
6159   {
6160     Yspring_wB,                         FALSE,  TRUE,
6161     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
6162   },
6163   {
6164     Yspring_kill_e,                     FALSE,  FALSE,
6165     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
6166   },
6167   {
6168     Yspring_kill_eB,                    FALSE,  TRUE,
6169     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
6170   },
6171   {
6172     Yspring_kill_w,                     FALSE,  FALSE,
6173     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
6174   },
6175   {
6176     Yspring_kill_wB,                    FALSE,  TRUE,
6177     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
6178   },
6179   {
6180     Xeater_n,                           TRUE,   FALSE,
6181     EL_YAMYAM_UP,                       -1, -1
6182   },
6183   {
6184     Xeater_e,                           TRUE,   FALSE,
6185     EL_YAMYAM_RIGHT,                    -1, -1
6186   },
6187   {
6188     Xeater_w,                           TRUE,   FALSE,
6189     EL_YAMYAM_LEFT,                     -1, -1
6190   },
6191   {
6192     Xeater_s,                           TRUE,   FALSE,
6193     EL_YAMYAM_DOWN,                     -1, -1
6194   },
6195   {
6196     Yeater_n,                           FALSE,  FALSE,
6197     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
6198   },
6199   {
6200     Yeater_nB,                          FALSE,  TRUE,
6201     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
6202   },
6203   {
6204     Yeater_e,                           FALSE,  FALSE,
6205     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
6206   },
6207   {
6208     Yeater_eB,                          FALSE,  TRUE,
6209     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
6210   },
6211   {
6212     Yeater_s,                           FALSE,  FALSE,
6213     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
6214   },
6215   {
6216     Yeater_sB,                          FALSE,  TRUE,
6217     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
6218   },
6219   {
6220     Yeater_w,                           FALSE,  FALSE,
6221     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
6222   },
6223   {
6224     Yeater_wB,                          FALSE,  TRUE,
6225     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
6226   },
6227   {
6228     Yeater_stone,                       FALSE,  FALSE,
6229     EL_YAMYAM,                          ACTION_SMASHED_BY_ROCK, -1
6230   },
6231   {
6232     Yeater_spring,                      FALSE,  FALSE,
6233     EL_YAMYAM,                          ACTION_SMASHED_BY_SPRING, -1
6234   },
6235   {
6236     Xalien,                             TRUE,   FALSE,
6237     EL_ROBOT,                           -1, -1
6238   },
6239   {
6240     Xalien_pause,                       FALSE,  FALSE,
6241     EL_ROBOT,                           -1, -1
6242   },
6243   {
6244     Yalien_n,                           FALSE,  FALSE,
6245     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
6246   },
6247   {
6248     Yalien_nB,                          FALSE,  TRUE,
6249     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
6250   },
6251   {
6252     Yalien_e,                           FALSE,  FALSE,
6253     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
6254   },
6255   {
6256     Yalien_eB,                          FALSE,  TRUE,
6257     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
6258   },
6259   {
6260     Yalien_s,                           FALSE,  FALSE,
6261     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
6262   },
6263   {
6264     Yalien_sB,                          FALSE,  TRUE,
6265     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
6266   },
6267   {
6268     Yalien_w,                           FALSE,  FALSE,
6269     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
6270   },
6271   {
6272     Yalien_wB,                          FALSE,  TRUE,
6273     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
6274   },
6275   {
6276     Yalien_stone,                       FALSE,  FALSE,
6277     EL_ROBOT,                           ACTION_SMASHED_BY_ROCK, -1
6278   },
6279   {
6280     Yalien_spring,                      FALSE,  FALSE,
6281     EL_ROBOT,                           ACTION_SMASHED_BY_SPRING, -1
6282   },
6283   {
6284     Xemerald,                           TRUE,   FALSE,
6285     EL_EMERALD,                         -1, -1
6286   },
6287   {
6288     Xemerald_pause,                     FALSE,  FALSE,
6289     EL_EMERALD,                         -1, -1
6290   },
6291   {
6292     Xemerald_fall,                      FALSE,  FALSE,
6293     EL_EMERALD,                         -1, -1
6294   },
6295   {
6296     Xemerald_shine,                     FALSE,  FALSE,
6297     EL_EMERALD,                         ACTION_TWINKLING, -1
6298   },
6299   {
6300     Yemerald_s,                         FALSE,  FALSE,
6301     EL_EMERALD,                         ACTION_FALLING, -1
6302   },
6303   {
6304     Yemerald_sB,                        FALSE,  TRUE,
6305     EL_EMERALD,                         ACTION_FALLING, -1
6306   },
6307   {
6308     Yemerald_e,                         FALSE,  FALSE,
6309     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
6310   },
6311   {
6312     Yemerald_eB,                        FALSE,  TRUE,
6313     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
6314   },
6315   {
6316     Yemerald_w,                         FALSE,  FALSE,
6317     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
6318   },
6319   {
6320     Yemerald_wB,                        FALSE,  TRUE,
6321     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
6322   },
6323   {
6324     Yemerald_eat,                       FALSE,  FALSE,
6325     EL_EMERALD,                         ACTION_COLLECTING, -1
6326   },
6327   {
6328     Yemerald_stone,                     FALSE,  FALSE,
6329     EL_NUT,                             ACTION_BREAKING, -1
6330   },
6331   {
6332     Xdiamond,                           TRUE,   FALSE,
6333     EL_DIAMOND,                         -1, -1
6334   },
6335   {
6336     Xdiamond_pause,                     FALSE,  FALSE,
6337     EL_DIAMOND,                         -1, -1
6338   },
6339   {
6340     Xdiamond_fall,                      FALSE,  FALSE,
6341     EL_DIAMOND,                         -1, -1
6342   },
6343   {
6344     Xdiamond_shine,                     FALSE,  FALSE,
6345     EL_DIAMOND,                         ACTION_TWINKLING, -1
6346   },
6347   {
6348     Ydiamond_s,                         FALSE,  FALSE,
6349     EL_DIAMOND,                         ACTION_FALLING, -1
6350   },
6351   {
6352     Ydiamond_sB,                        FALSE,  TRUE,
6353     EL_DIAMOND,                         ACTION_FALLING, -1
6354   },
6355   {
6356     Ydiamond_e,                         FALSE,  FALSE,
6357     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
6358   },
6359   {
6360     Ydiamond_eB,                        FALSE,  TRUE,
6361     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
6362   },
6363   {
6364     Ydiamond_w,                         FALSE,  FALSE,
6365     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
6366   },
6367   {
6368     Ydiamond_wB,                        FALSE,  TRUE,
6369     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
6370   },
6371   {
6372     Ydiamond_eat,                       FALSE,  FALSE,
6373     EL_DIAMOND,                         ACTION_COLLECTING, -1
6374   },
6375   {
6376     Ydiamond_stone,                     FALSE,  FALSE,
6377     EL_DIAMOND,                         ACTION_SMASHED_BY_ROCK, -1
6378   },
6379   {
6380     Xdrip_fall,                         TRUE,   FALSE,
6381     EL_AMOEBA_DROP,                     -1, -1
6382   },
6383   {
6384     Xdrip_stretch,                      FALSE,  FALSE,
6385     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6386   },
6387   {
6388     Xdrip_stretchB,                     FALSE,  TRUE,
6389     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6390   },
6391   {
6392     Xdrip_eat,                          FALSE,  FALSE,
6393     EL_AMOEBA_DROP,                     ACTION_GROWING, -1
6394   },
6395   {
6396     Ydrip_s1,                           FALSE,  FALSE,
6397     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6398   },
6399   {
6400     Ydrip_s1B,                          FALSE,  TRUE,
6401     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6402   },
6403   {
6404     Ydrip_s2,                           FALSE,  FALSE,
6405     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6406   },
6407   {
6408     Ydrip_s2B,                          FALSE,  TRUE,
6409     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6410   },
6411   {
6412     Xbomb,                              TRUE,   FALSE,
6413     EL_BOMB,                            -1, -1
6414   },
6415   {
6416     Xbomb_pause,                        FALSE,  FALSE,
6417     EL_BOMB,                            -1, -1
6418   },
6419   {
6420     Xbomb_fall,                         FALSE,  FALSE,
6421     EL_BOMB,                            -1, -1
6422   },
6423   {
6424     Ybomb_s,                            FALSE,  FALSE,
6425     EL_BOMB,                            ACTION_FALLING, -1
6426   },
6427   {
6428     Ybomb_sB,                           FALSE,  TRUE,
6429     EL_BOMB,                            ACTION_FALLING, -1
6430   },
6431   {
6432     Ybomb_e,                            FALSE,  FALSE,
6433     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
6434   },
6435   {
6436     Ybomb_eB,                           FALSE,  TRUE,
6437     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
6438   },
6439   {
6440     Ybomb_w,                            FALSE,  FALSE,
6441     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
6442   },
6443   {
6444     Ybomb_wB,                           FALSE,  TRUE,
6445     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
6446   },
6447   {
6448     Ybomb_eat,                          FALSE,  FALSE,
6449     EL_BOMB,                            ACTION_ACTIVATING, -1
6450   },
6451   {
6452     Xballoon,                           TRUE,   FALSE,
6453     EL_BALLOON,                         -1, -1
6454   },
6455   {
6456     Yballoon_n,                         FALSE,  FALSE,
6457     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
6458   },
6459   {
6460     Yballoon_nB,                        FALSE,  TRUE,
6461     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
6462   },
6463   {
6464     Yballoon_e,                         FALSE,  FALSE,
6465     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
6466   },
6467   {
6468     Yballoon_eB,                        FALSE,  TRUE,
6469     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
6470   },
6471   {
6472     Yballoon_s,                         FALSE,  FALSE,
6473     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
6474   },
6475   {
6476     Yballoon_sB,                        FALSE,  TRUE,
6477     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
6478   },
6479   {
6480     Yballoon_w,                         FALSE,  FALSE,
6481     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
6482   },
6483   {
6484     Yballoon_wB,                        FALSE,  TRUE,
6485     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
6486   },
6487   {
6488     Xgrass,                             TRUE,   FALSE,
6489     EL_EMC_GRASS,                       -1, -1
6490   },
6491   {
6492     Ygrass_nB,                          FALSE,  FALSE,
6493     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_UP
6494   },
6495   {
6496     Ygrass_eB,                          FALSE,  FALSE,
6497     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_RIGHT
6498   },
6499   {
6500     Ygrass_sB,                          FALSE,  FALSE,
6501     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_DOWN
6502   },
6503   {
6504     Ygrass_wB,                          FALSE,  FALSE,
6505     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_LEFT
6506   },
6507   {
6508     Xdirt,                              TRUE,   FALSE,
6509     EL_SAND,                            -1, -1
6510   },
6511   {
6512     Ydirt_nB,                           FALSE,  FALSE,
6513     EL_SAND,                            ACTION_DIGGING, MV_BIT_UP
6514   },
6515   {
6516     Ydirt_eB,                           FALSE,  FALSE,
6517     EL_SAND,                            ACTION_DIGGING, MV_BIT_RIGHT
6518   },
6519   {
6520     Ydirt_sB,                           FALSE,  FALSE,
6521     EL_SAND,                            ACTION_DIGGING, MV_BIT_DOWN
6522   },
6523   {
6524     Ydirt_wB,                           FALSE,  FALSE,
6525     EL_SAND,                            ACTION_DIGGING, MV_BIT_LEFT
6526   },
6527   {
6528     Xacid_ne,                           TRUE,   FALSE,
6529     EL_ACID_POOL_TOPRIGHT,              -1, -1
6530   },
6531   {
6532     Xacid_se,                           TRUE,   FALSE,
6533     EL_ACID_POOL_BOTTOMRIGHT,           -1, -1
6534   },
6535   {
6536     Xacid_s,                            TRUE,   FALSE,
6537     EL_ACID_POOL_BOTTOM,                -1, -1
6538   },
6539   {
6540     Xacid_sw,                           TRUE,   FALSE,
6541     EL_ACID_POOL_BOTTOMLEFT,            -1, -1
6542   },
6543   {
6544     Xacid_nw,                           TRUE,   FALSE,
6545     EL_ACID_POOL_TOPLEFT,               -1, -1
6546   },
6547   {
6548     Xacid_1,                            TRUE,   FALSE,
6549     EL_ACID,                            -1, -1
6550   },
6551   {
6552     Xacid_2,                            FALSE,  FALSE,
6553     EL_ACID,                            -1, -1
6554   },
6555   {
6556     Xacid_3,                            FALSE,  FALSE,
6557     EL_ACID,                            -1, -1
6558   },
6559   {
6560     Xacid_4,                            FALSE,  FALSE,
6561     EL_ACID,                            -1, -1
6562   },
6563   {
6564     Xacid_5,                            FALSE,  FALSE,
6565     EL_ACID,                            -1, -1
6566   },
6567   {
6568     Xacid_6,                            FALSE,  FALSE,
6569     EL_ACID,                            -1, -1
6570   },
6571   {
6572     Xacid_7,                            FALSE,  FALSE,
6573     EL_ACID,                            -1, -1
6574   },
6575   {
6576     Xacid_8,                            FALSE,  FALSE,
6577     EL_ACID,                            -1, -1
6578   },
6579   {
6580     Xball_1,                            TRUE,   FALSE,
6581     EL_EMC_MAGIC_BALL,                  -1, -1
6582   },
6583   {
6584     Xball_1B,                           FALSE,  FALSE,
6585     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
6586   },
6587   {
6588     Xball_2,                            FALSE,  FALSE,
6589     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
6590   },
6591   {
6592     Xball_2B,                           FALSE,  FALSE,
6593     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
6594   },
6595   {
6596     Yball_eat,                          FALSE,  FALSE,
6597     EL_EMC_MAGIC_BALL,                  ACTION_DROPPING, -1
6598   },
6599   {
6600     Ykey_1_eat,                         FALSE,  FALSE,
6601     EL_EM_KEY_1,                        ACTION_COLLECTING, -1
6602   },
6603   {
6604     Ykey_2_eat,                         FALSE,  FALSE,
6605     EL_EM_KEY_2,                        ACTION_COLLECTING, -1
6606   },
6607   {
6608     Ykey_3_eat,                         FALSE,  FALSE,
6609     EL_EM_KEY_3,                        ACTION_COLLECTING, -1
6610   },
6611   {
6612     Ykey_4_eat,                         FALSE,  FALSE,
6613     EL_EM_KEY_4,                        ACTION_COLLECTING, -1
6614   },
6615   {
6616     Ykey_5_eat,                         FALSE,  FALSE,
6617     EL_EMC_KEY_5,                       ACTION_COLLECTING, -1
6618   },
6619   {
6620     Ykey_6_eat,                         FALSE,  FALSE,
6621     EL_EMC_KEY_6,                       ACTION_COLLECTING, -1
6622   },
6623   {
6624     Ykey_7_eat,                         FALSE,  FALSE,
6625     EL_EMC_KEY_7,                       ACTION_COLLECTING, -1
6626   },
6627   {
6628     Ykey_8_eat,                         FALSE,  FALSE,
6629     EL_EMC_KEY_8,                       ACTION_COLLECTING, -1
6630   },
6631   {
6632     Ylenses_eat,                        FALSE,  FALSE,
6633     EL_EMC_LENSES,                      ACTION_COLLECTING, -1
6634   },
6635   {
6636     Ymagnify_eat,                       FALSE,  FALSE,
6637     EL_EMC_MAGNIFIER,                   ACTION_COLLECTING, -1
6638   },
6639   {
6640     Ygrass_eat,                         FALSE,  FALSE,
6641     EL_EMC_GRASS,                       ACTION_SNAPPING, -1
6642   },
6643   {
6644     Ydirt_eat,                          FALSE,  FALSE,
6645     EL_SAND,                            ACTION_SNAPPING, -1
6646   },
6647   {
6648     Xgrow_ns,                           TRUE,   FALSE,
6649     EL_EXPANDABLE_WALL_VERTICAL,        -1, -1
6650   },
6651   {
6652     Ygrow_ns_eat,                       FALSE,  FALSE,
6653     EL_EXPANDABLE_WALL_VERTICAL,        ACTION_GROWING, -1
6654   },
6655   {
6656     Xgrow_ew,                           TRUE,   FALSE,
6657     EL_EXPANDABLE_WALL_HORIZONTAL,      -1, -1
6658   },
6659   {
6660     Ygrow_ew_eat,                       FALSE,  FALSE,
6661     EL_EXPANDABLE_WALL_HORIZONTAL,      ACTION_GROWING, -1
6662   },
6663   {
6664     Xwonderwall,                        TRUE,   FALSE,
6665     EL_MAGIC_WALL,                      -1, -1
6666   },
6667   {
6668     XwonderwallB,                       FALSE,  FALSE,
6669     EL_MAGIC_WALL,                      ACTION_ACTIVE, -1
6670   },
6671   {
6672     Xamoeba_1,                          TRUE,   FALSE,
6673     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6674   },
6675   {
6676     Xamoeba_2,                          FALSE,  FALSE,
6677     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6678   },
6679   {
6680     Xamoeba_3,                          FALSE,  FALSE,
6681     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6682   },
6683   {
6684     Xamoeba_4,                          FALSE,  FALSE,
6685     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6686   },
6687   {
6688     Xamoeba_5,                          TRUE,   FALSE,
6689     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6690   },
6691   {
6692     Xamoeba_6,                          FALSE,  FALSE,
6693     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6694   },
6695   {
6696     Xamoeba_7,                          FALSE,  FALSE,
6697     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6698   },
6699   {
6700     Xamoeba_8,                          FALSE,  FALSE,
6701     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6702   },
6703   {
6704     Xdoor_1,                            TRUE,   FALSE,
6705     EL_EM_GATE_1,                       -1, -1
6706   },
6707   {
6708     Xdoor_2,                            TRUE,   FALSE,
6709     EL_EM_GATE_2,                       -1, -1
6710   },
6711   {
6712     Xdoor_3,                            TRUE,   FALSE,
6713     EL_EM_GATE_3,                       -1, -1
6714   },
6715   {
6716     Xdoor_4,                            TRUE,   FALSE,
6717     EL_EM_GATE_4,                       -1, -1
6718   },
6719   {
6720     Xdoor_5,                            TRUE,   FALSE,
6721     EL_EMC_GATE_5,                      -1, -1
6722   },
6723   {
6724     Xdoor_6,                            TRUE,   FALSE,
6725     EL_EMC_GATE_6,                      -1, -1
6726   },
6727   {
6728     Xdoor_7,                            TRUE,   FALSE,
6729     EL_EMC_GATE_7,                      -1, -1
6730   },
6731   {
6732     Xdoor_8,                            TRUE,   FALSE,
6733     EL_EMC_GATE_8,                      -1, -1
6734   },
6735   {
6736     Xkey_1,                             TRUE,   FALSE,
6737     EL_EM_KEY_1,                        -1, -1
6738   },
6739   {
6740     Xkey_2,                             TRUE,   FALSE,
6741     EL_EM_KEY_2,                        -1, -1
6742   },
6743   {
6744     Xkey_3,                             TRUE,   FALSE,
6745     EL_EM_KEY_3,                        -1, -1
6746   },
6747   {
6748     Xkey_4,                             TRUE,   FALSE,
6749     EL_EM_KEY_4,                        -1, -1
6750   },
6751   {
6752     Xkey_5,                             TRUE,   FALSE,
6753     EL_EMC_KEY_5,                       -1, -1
6754   },
6755   {
6756     Xkey_6,                             TRUE,   FALSE,
6757     EL_EMC_KEY_6,                       -1, -1
6758   },
6759   {
6760     Xkey_7,                             TRUE,   FALSE,
6761     EL_EMC_KEY_7,                       -1, -1
6762   },
6763   {
6764     Xkey_8,                             TRUE,   FALSE,
6765     EL_EMC_KEY_8,                       -1, -1
6766   },
6767   {
6768     Xwind_n,                            TRUE,   FALSE,
6769     EL_BALLOON_SWITCH_UP,               -1, -1
6770   },
6771   {
6772     Xwind_e,                            TRUE,   FALSE,
6773     EL_BALLOON_SWITCH_RIGHT,            -1, -1
6774   },
6775   {
6776     Xwind_s,                            TRUE,   FALSE,
6777     EL_BALLOON_SWITCH_DOWN,             -1, -1
6778   },
6779   {
6780     Xwind_w,                            TRUE,   FALSE,
6781     EL_BALLOON_SWITCH_LEFT,             -1, -1
6782   },
6783   {
6784     Xwind_nesw,                         TRUE,   FALSE,
6785     EL_BALLOON_SWITCH_ANY,              -1, -1
6786   },
6787   {
6788     Xwind_stop,                         TRUE,   FALSE,
6789     EL_BALLOON_SWITCH_NONE,             -1, -1
6790   },
6791   {
6792     Xexit,                              TRUE,   FALSE,
6793     EL_EM_EXIT_CLOSED,                  -1, -1
6794   },
6795   {
6796     Xexit_1,                            TRUE,   FALSE,
6797     EL_EM_EXIT_OPEN,                    -1, -1
6798   },
6799   {
6800     Xexit_2,                            FALSE,  FALSE,
6801     EL_EM_EXIT_OPEN,                    -1, -1
6802   },
6803   {
6804     Xexit_3,                            FALSE,  FALSE,
6805     EL_EM_EXIT_OPEN,                    -1, -1
6806   },
6807   {
6808     Xdynamite,                          TRUE,   FALSE,
6809     EL_EM_DYNAMITE,                     -1, -1
6810   },
6811   {
6812     Ydynamite_eat,                      FALSE,  FALSE,
6813     EL_EM_DYNAMITE,                     ACTION_COLLECTING, -1
6814   },
6815   {
6816     Xdynamite_1,                        TRUE,   FALSE,
6817     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6818   },
6819   {
6820     Xdynamite_2,                        FALSE,  FALSE,
6821     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6822   },
6823   {
6824     Xdynamite_3,                        FALSE,  FALSE,
6825     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6826   },
6827   {
6828     Xdynamite_4,                        FALSE,  FALSE,
6829     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6830   },
6831   {
6832     Xbumper,                            TRUE,   FALSE,
6833     EL_EMC_SPRING_BUMPER,               -1, -1
6834   },
6835   {
6836     XbumperB,                           FALSE,  FALSE,
6837     EL_EMC_SPRING_BUMPER,               ACTION_ACTIVE, -1
6838   },
6839   {
6840     Xwheel,                             TRUE,   FALSE,
6841     EL_ROBOT_WHEEL,                     -1, -1
6842   },
6843   {
6844     XwheelB,                            FALSE,  FALSE,
6845     EL_ROBOT_WHEEL,                     ACTION_ACTIVE, -1
6846   },
6847   {
6848     Xswitch,                            TRUE,   FALSE,
6849     EL_EMC_MAGIC_BALL_SWITCH,           -1, -1
6850   },
6851   {
6852     XswitchB,                           FALSE,  FALSE,
6853     EL_EMC_MAGIC_BALL_SWITCH,           ACTION_ACTIVE, -1
6854   },
6855   {
6856     Xsand,                              TRUE,   FALSE,
6857     EL_QUICKSAND_EMPTY,                 -1, -1
6858   },
6859   {
6860     Xsand_stone,                        TRUE,   FALSE,
6861     EL_QUICKSAND_FULL,                  -1, -1
6862   },
6863   {
6864     Xsand_stonein_1,                    FALSE,  TRUE,
6865     EL_ROCK,                            ACTION_FILLING, -1
6866   },
6867   {
6868     Xsand_stonein_2,                    FALSE,  TRUE,
6869     EL_ROCK,                            ACTION_FILLING, -1
6870   },
6871   {
6872     Xsand_stonein_3,                    FALSE,  TRUE,
6873     EL_ROCK,                            ACTION_FILLING, -1
6874   },
6875   {
6876     Xsand_stonein_4,                    FALSE,  TRUE,
6877     EL_ROCK,                            ACTION_FILLING, -1
6878   },
6879   {
6880     Xsand_stonesand_1,                  FALSE,  FALSE,
6881     EL_QUICKSAND_EMPTYING,              -1, -1
6882   },
6883   {
6884     Xsand_stonesand_2,                  FALSE,  FALSE,
6885     EL_QUICKSAND_EMPTYING,              -1, -1
6886   },
6887   {
6888     Xsand_stonesand_3,                  FALSE,  FALSE,
6889     EL_QUICKSAND_EMPTYING,              -1, -1
6890   },
6891   {
6892     Xsand_stonesand_4,                  FALSE,  FALSE,
6893     EL_QUICKSAND_EMPTYING,              -1, -1
6894   },
6895   {
6896     Xsand_stonesand_quickout_1,         FALSE,  FALSE,
6897     EL_QUICKSAND_EMPTYING,              -1, -1
6898   },
6899   {
6900     Xsand_stonesand_quickout_2,         FALSE,  FALSE,
6901     EL_QUICKSAND_EMPTYING,              -1, -1
6902   },
6903   {
6904     Xsand_stoneout_1,                   FALSE,  FALSE,
6905     EL_ROCK,                            ACTION_EMPTYING, -1
6906   },
6907   {
6908     Xsand_stoneout_2,                   FALSE,  FALSE,
6909     EL_ROCK,                            ACTION_EMPTYING, -1
6910   },
6911   {
6912     Xsand_sandstone_1,                  FALSE,  FALSE,
6913     EL_QUICKSAND_FILLING,               -1, -1
6914   },
6915   {
6916     Xsand_sandstone_2,                  FALSE,  FALSE,
6917     EL_QUICKSAND_FILLING,               -1, -1
6918   },
6919   {
6920     Xsand_sandstone_3,                  FALSE,  FALSE,
6921     EL_QUICKSAND_FILLING,               -1, -1
6922   },
6923   {
6924     Xsand_sandstone_4,                  FALSE,  FALSE,
6925     EL_QUICKSAND_FILLING,               -1, -1
6926   },
6927   {
6928     Xplant,                             TRUE,   FALSE,
6929     EL_EMC_PLANT,                       -1, -1
6930   },
6931   {
6932     Yplant,                             FALSE,  FALSE,
6933     EL_EMC_PLANT,                       -1, -1
6934   },
6935   {
6936     Xlenses,                            TRUE,   FALSE,
6937     EL_EMC_LENSES,                      -1, -1
6938   },
6939   {
6940     Xmagnify,                           TRUE,   FALSE,
6941     EL_EMC_MAGNIFIER,                   -1, -1
6942   },
6943   {
6944     Xdripper,                           TRUE,   FALSE,
6945     EL_EMC_DRIPPER,                     -1, -1
6946   },
6947   {
6948     XdripperB,                          FALSE,  FALSE,
6949     EL_EMC_DRIPPER,                     ACTION_ACTIVE, -1
6950   },
6951   {
6952     Xfake_blank,                        TRUE,   FALSE,
6953     EL_INVISIBLE_WALL,                  -1, -1
6954   },
6955   {
6956     Xfake_blankB,                       FALSE,  FALSE,
6957     EL_INVISIBLE_WALL,                  ACTION_ACTIVE, -1
6958   },
6959   {
6960     Xfake_grass,                        TRUE,   FALSE,
6961     EL_EMC_FAKE_GRASS,                  -1, -1
6962   },
6963   {
6964     Xfake_grassB,                       FALSE,  FALSE,
6965     EL_EMC_FAKE_GRASS,                  ACTION_ACTIVE, -1
6966   },
6967   {
6968     Xfake_door_1,                       TRUE,   FALSE,
6969     EL_EM_GATE_1_GRAY,                  -1, -1
6970   },
6971   {
6972     Xfake_door_2,                       TRUE,   FALSE,
6973     EL_EM_GATE_2_GRAY,                  -1, -1
6974   },
6975   {
6976     Xfake_door_3,                       TRUE,   FALSE,
6977     EL_EM_GATE_3_GRAY,                  -1, -1
6978   },
6979   {
6980     Xfake_door_4,                       TRUE,   FALSE,
6981     EL_EM_GATE_4_GRAY,                  -1, -1
6982   },
6983   {
6984     Xfake_door_5,                       TRUE,   FALSE,
6985     EL_EMC_GATE_5_GRAY,                 -1, -1
6986   },
6987   {
6988     Xfake_door_6,                       TRUE,   FALSE,
6989     EL_EMC_GATE_6_GRAY,                 -1, -1
6990   },
6991   {
6992     Xfake_door_7,                       TRUE,   FALSE,
6993     EL_EMC_GATE_7_GRAY,                 -1, -1
6994   },
6995   {
6996     Xfake_door_8,                       TRUE,   FALSE,
6997     EL_EMC_GATE_8_GRAY,                 -1, -1
6998   },
6999   {
7000     Xfake_acid_1,                       TRUE,   FALSE,
7001     EL_EMC_FAKE_ACID,                   -1, -1
7002   },
7003   {
7004     Xfake_acid_2,                       FALSE,  FALSE,
7005     EL_EMC_FAKE_ACID,                   -1, -1
7006   },
7007   {
7008     Xfake_acid_3,                       FALSE,  FALSE,
7009     EL_EMC_FAKE_ACID,                   -1, -1
7010   },
7011   {
7012     Xfake_acid_4,                       FALSE,  FALSE,
7013     EL_EMC_FAKE_ACID,                   -1, -1
7014   },
7015   {
7016     Xfake_acid_5,                       FALSE,  FALSE,
7017     EL_EMC_FAKE_ACID,                   -1, -1
7018   },
7019   {
7020     Xfake_acid_6,                       FALSE,  FALSE,
7021     EL_EMC_FAKE_ACID,                   -1, -1
7022   },
7023   {
7024     Xfake_acid_7,                       FALSE,  FALSE,
7025     EL_EMC_FAKE_ACID,                   -1, -1
7026   },
7027   {
7028     Xfake_acid_8,                       FALSE,  FALSE,
7029     EL_EMC_FAKE_ACID,                   -1, -1
7030   },
7031   {
7032     Xsteel_1,                           TRUE,   FALSE,
7033     EL_STEELWALL,                       -1, -1
7034   },
7035   {
7036     Xsteel_2,                           TRUE,   FALSE,
7037     EL_EMC_STEELWALL_2,                 -1, -1
7038   },
7039   {
7040     Xsteel_3,                           TRUE,   FALSE,
7041     EL_EMC_STEELWALL_3,                 -1, -1
7042   },
7043   {
7044     Xsteel_4,                           TRUE,   FALSE,
7045     EL_EMC_STEELWALL_4,                 -1, -1
7046   },
7047   {
7048     Xwall_1,                            TRUE,   FALSE,
7049     EL_WALL,                            -1, -1
7050   },
7051   {
7052     Xwall_2,                            TRUE,   FALSE,
7053     EL_EMC_WALL_14,                     -1, -1
7054   },
7055   {
7056     Xwall_3,                            TRUE,   FALSE,
7057     EL_EMC_WALL_15,                     -1, -1
7058   },
7059   {
7060     Xwall_4,                            TRUE,   FALSE,
7061     EL_EMC_WALL_16,                     -1, -1
7062   },
7063   {
7064     Xround_wall_1,                      TRUE,   FALSE,
7065     EL_WALL_SLIPPERY,                   -1, -1
7066   },
7067   {
7068     Xround_wall_2,                      TRUE,   FALSE,
7069     EL_EMC_WALL_SLIPPERY_2,             -1, -1
7070   },
7071   {
7072     Xround_wall_3,                      TRUE,   FALSE,
7073     EL_EMC_WALL_SLIPPERY_3,             -1, -1
7074   },
7075   {
7076     Xround_wall_4,                      TRUE,   FALSE,
7077     EL_EMC_WALL_SLIPPERY_4,             -1, -1
7078   },
7079   {
7080     Xdecor_1,                           TRUE,   FALSE,
7081     EL_EMC_WALL_8,                      -1, -1
7082   },
7083   {
7084     Xdecor_2,                           TRUE,   FALSE,
7085     EL_EMC_WALL_6,                      -1, -1
7086   },
7087   {
7088     Xdecor_3,                           TRUE,   FALSE,
7089     EL_EMC_WALL_4,                      -1, -1
7090   },
7091   {
7092     Xdecor_4,                           TRUE,   FALSE,
7093     EL_EMC_WALL_7,                      -1, -1
7094   },
7095   {
7096     Xdecor_5,                           TRUE,   FALSE,
7097     EL_EMC_WALL_5,                      -1, -1
7098   },
7099   {
7100     Xdecor_6,                           TRUE,   FALSE,
7101     EL_EMC_WALL_9,                      -1, -1
7102   },
7103   {
7104     Xdecor_7,                           TRUE,   FALSE,
7105     EL_EMC_WALL_10,                     -1, -1
7106   },
7107   {
7108     Xdecor_8,                           TRUE,   FALSE,
7109     EL_EMC_WALL_1,                      -1, -1
7110   },
7111   {
7112     Xdecor_9,                           TRUE,   FALSE,
7113     EL_EMC_WALL_2,                      -1, -1
7114   },
7115   {
7116     Xdecor_10,                          TRUE,   FALSE,
7117     EL_EMC_WALL_3,                      -1, -1
7118   },
7119   {
7120     Xdecor_11,                          TRUE,   FALSE,
7121     EL_EMC_WALL_11,                     -1, -1
7122   },
7123   {
7124     Xdecor_12,                          TRUE,   FALSE,
7125     EL_EMC_WALL_12,                     -1, -1
7126   },
7127   {
7128     Xalpha_0,                           TRUE,   FALSE,
7129     EL_CHAR('0'),                       -1, -1
7130   },
7131   {
7132     Xalpha_1,                           TRUE,   FALSE,
7133     EL_CHAR('1'),                       -1, -1
7134   },
7135   {
7136     Xalpha_2,                           TRUE,   FALSE,
7137     EL_CHAR('2'),                       -1, -1
7138   },
7139   {
7140     Xalpha_3,                           TRUE,   FALSE,
7141     EL_CHAR('3'),                       -1, -1
7142   },
7143   {
7144     Xalpha_4,                           TRUE,   FALSE,
7145     EL_CHAR('4'),                       -1, -1
7146   },
7147   {
7148     Xalpha_5,                           TRUE,   FALSE,
7149     EL_CHAR('5'),                       -1, -1
7150   },
7151   {
7152     Xalpha_6,                           TRUE,   FALSE,
7153     EL_CHAR('6'),                       -1, -1
7154   },
7155   {
7156     Xalpha_7,                           TRUE,   FALSE,
7157     EL_CHAR('7'),                       -1, -1
7158   },
7159   {
7160     Xalpha_8,                           TRUE,   FALSE,
7161     EL_CHAR('8'),                       -1, -1
7162   },
7163   {
7164     Xalpha_9,                           TRUE,   FALSE,
7165     EL_CHAR('9'),                       -1, -1
7166   },
7167   {
7168     Xalpha_excla,                       TRUE,   FALSE,
7169     EL_CHAR('!'),                       -1, -1
7170   },
7171   {
7172     Xalpha_quote,                       TRUE,   FALSE,
7173     EL_CHAR('"'),                       -1, -1
7174   },
7175   {
7176     Xalpha_comma,                       TRUE,   FALSE,
7177     EL_CHAR(','),                       -1, -1
7178   },
7179   {
7180     Xalpha_minus,                       TRUE,   FALSE,
7181     EL_CHAR('-'),                       -1, -1
7182   },
7183   {
7184     Xalpha_perio,                       TRUE,   FALSE,
7185     EL_CHAR('.'),                       -1, -1
7186   },
7187   {
7188     Xalpha_colon,                       TRUE,   FALSE,
7189     EL_CHAR(':'),                       -1, -1
7190   },
7191   {
7192     Xalpha_quest,                       TRUE,   FALSE,
7193     EL_CHAR('?'),                       -1, -1
7194   },
7195   {
7196     Xalpha_a,                           TRUE,   FALSE,
7197     EL_CHAR('A'),                       -1, -1
7198   },
7199   {
7200     Xalpha_b,                           TRUE,   FALSE,
7201     EL_CHAR('B'),                       -1, -1
7202   },
7203   {
7204     Xalpha_c,                           TRUE,   FALSE,
7205     EL_CHAR('C'),                       -1, -1
7206   },
7207   {
7208     Xalpha_d,                           TRUE,   FALSE,
7209     EL_CHAR('D'),                       -1, -1
7210   },
7211   {
7212     Xalpha_e,                           TRUE,   FALSE,
7213     EL_CHAR('E'),                       -1, -1
7214   },
7215   {
7216     Xalpha_f,                           TRUE,   FALSE,
7217     EL_CHAR('F'),                       -1, -1
7218   },
7219   {
7220     Xalpha_g,                           TRUE,   FALSE,
7221     EL_CHAR('G'),                       -1, -1
7222   },
7223   {
7224     Xalpha_h,                           TRUE,   FALSE,
7225     EL_CHAR('H'),                       -1, -1
7226   },
7227   {
7228     Xalpha_i,                           TRUE,   FALSE,
7229     EL_CHAR('I'),                       -1, -1
7230   },
7231   {
7232     Xalpha_j,                           TRUE,   FALSE,
7233     EL_CHAR('J'),                       -1, -1
7234   },
7235   {
7236     Xalpha_k,                           TRUE,   FALSE,
7237     EL_CHAR('K'),                       -1, -1
7238   },
7239   {
7240     Xalpha_l,                           TRUE,   FALSE,
7241     EL_CHAR('L'),                       -1, -1
7242   },
7243   {
7244     Xalpha_m,                           TRUE,   FALSE,
7245     EL_CHAR('M'),                       -1, -1
7246   },
7247   {
7248     Xalpha_n,                           TRUE,   FALSE,
7249     EL_CHAR('N'),                       -1, -1
7250   },
7251   {
7252     Xalpha_o,                           TRUE,   FALSE,
7253     EL_CHAR('O'),                       -1, -1
7254   },
7255   {
7256     Xalpha_p,                           TRUE,   FALSE,
7257     EL_CHAR('P'),                       -1, -1
7258   },
7259   {
7260     Xalpha_q,                           TRUE,   FALSE,
7261     EL_CHAR('Q'),                       -1, -1
7262   },
7263   {
7264     Xalpha_r,                           TRUE,   FALSE,
7265     EL_CHAR('R'),                       -1, -1
7266   },
7267   {
7268     Xalpha_s,                           TRUE,   FALSE,
7269     EL_CHAR('S'),                       -1, -1
7270   },
7271   {
7272     Xalpha_t,                           TRUE,   FALSE,
7273     EL_CHAR('T'),                       -1, -1
7274   },
7275   {
7276     Xalpha_u,                           TRUE,   FALSE,
7277     EL_CHAR('U'),                       -1, -1
7278   },
7279   {
7280     Xalpha_v,                           TRUE,   FALSE,
7281     EL_CHAR('V'),                       -1, -1
7282   },
7283   {
7284     Xalpha_w,                           TRUE,   FALSE,
7285     EL_CHAR('W'),                       -1, -1
7286   },
7287   {
7288     Xalpha_x,                           TRUE,   FALSE,
7289     EL_CHAR('X'),                       -1, -1
7290   },
7291   {
7292     Xalpha_y,                           TRUE,   FALSE,
7293     EL_CHAR('Y'),                       -1, -1
7294   },
7295   {
7296     Xalpha_z,                           TRUE,   FALSE,
7297     EL_CHAR('Z'),                       -1, -1
7298   },
7299   {
7300     Xalpha_arrow_e,                     TRUE,   FALSE,
7301     EL_CHAR('>'),                       -1, -1
7302   },
7303   {
7304     Xalpha_arrow_w,                     TRUE,   FALSE,
7305     EL_CHAR('<'),                       -1, -1
7306   },
7307   {
7308     Xalpha_copyr,                       TRUE,   FALSE,
7309     EL_CHAR(CHAR_BYTE_COPYRIGHT),       -1, -1
7310   },
7311
7312   {
7313     Xboom_bug,                          FALSE,  FALSE,
7314     EL_BUG,                             ACTION_EXPLODING, -1
7315   },
7316   {
7317     Xboom_bomb,                         FALSE,  FALSE,
7318     EL_BOMB,                            ACTION_EXPLODING, -1
7319   },
7320   {
7321     Xboom_android,                      FALSE,  FALSE,
7322     EL_EMC_ANDROID,                     ACTION_OTHER, -1
7323   },
7324   {
7325     Xboom_1,                            FALSE,  FALSE,
7326     EL_DEFAULT,                         ACTION_EXPLODING, -1
7327   },
7328   {
7329     Xboom_2,                            FALSE,  FALSE,
7330     EL_DEFAULT,                         ACTION_EXPLODING, -1
7331   },
7332   {
7333     Znormal,                            FALSE,  FALSE,
7334     EL_EMPTY,                           -1, -1
7335   },
7336   {
7337     Zdynamite,                          FALSE,  FALSE,
7338     EL_EMPTY,                           -1, -1
7339   },
7340   {
7341     Zplayer,                            FALSE,  FALSE,
7342     EL_EMPTY,                           -1, -1
7343   },
7344   {
7345     ZBORDER,                            FALSE,  FALSE,
7346     EL_EMPTY,                           -1, -1
7347   },
7348
7349   {
7350     -1,                                 FALSE,  FALSE,
7351     -1,                                 -1, -1
7352   }
7353 };
7354
7355 static struct Mapping_EM_to_RND_player
7356 {
7357   int action_em;
7358   int player_nr;
7359
7360   int element_rnd;
7361   int action;
7362   int direction;
7363 }
7364 em_player_mapping_list[] =
7365 {
7366   {
7367     SPR_walk + 0,                       0,
7368     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_UP,
7369   },
7370   {
7371     SPR_walk + 1,                       0,
7372     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_RIGHT,
7373   },
7374   {
7375     SPR_walk + 2,                       0,
7376     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_DOWN,
7377   },
7378   {
7379     SPR_walk + 3,                       0,
7380     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_LEFT,
7381   },
7382   {
7383     SPR_push + 0,                       0,
7384     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_UP,
7385   },
7386   {
7387     SPR_push + 1,                       0,
7388     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_RIGHT,
7389   },
7390   {
7391     SPR_push + 2,                       0,
7392     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_DOWN,
7393   },
7394   {
7395     SPR_push + 3,                       0,
7396     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_LEFT,
7397   },
7398   {
7399     SPR_spray + 0,                      0,
7400     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_UP,
7401   },
7402   {
7403     SPR_spray + 1,                      0,
7404     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7405   },
7406   {
7407     SPR_spray + 2,                      0,
7408     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_DOWN,
7409   },
7410   {
7411     SPR_spray + 3,                      0,
7412     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_LEFT,
7413   },
7414   {
7415     SPR_walk + 0,                       1,
7416     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_UP,
7417   },
7418   {
7419     SPR_walk + 1,                       1,
7420     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_RIGHT,
7421   },
7422   {
7423     SPR_walk + 2,                       1,
7424     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_DOWN,
7425   },
7426   {
7427     SPR_walk + 3,                       1,
7428     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_LEFT,
7429   },
7430   {
7431     SPR_push + 0,                       1,
7432     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_UP,
7433   },
7434   {
7435     SPR_push + 1,                       1,
7436     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_RIGHT,
7437   },
7438   {
7439     SPR_push + 2,                       1,
7440     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_DOWN,
7441   },
7442   {
7443     SPR_push + 3,                       1,
7444     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_LEFT,
7445   },
7446   {
7447     SPR_spray + 0,                      1,
7448     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_UP,
7449   },
7450   {
7451     SPR_spray + 1,                      1,
7452     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7453   },
7454   {
7455     SPR_spray + 2,                      1,
7456     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_DOWN,
7457   },
7458   {
7459     SPR_spray + 3,                      1,
7460     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_LEFT,
7461   },
7462   {
7463     SPR_still,                          0,
7464     EL_PLAYER_1,                        ACTION_DEFAULT, -1,
7465   },
7466   {
7467     SPR_still,                          1,
7468     EL_PLAYER_2,                        ACTION_DEFAULT, -1,
7469   },
7470   {
7471     SPR_walk + 0,                       2,
7472     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_UP,
7473   },
7474   {
7475     SPR_walk + 1,                       2,
7476     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_RIGHT,
7477   },
7478   {
7479     SPR_walk + 2,                       2,
7480     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_DOWN,
7481   },
7482   {
7483     SPR_walk + 3,                       2,
7484     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_LEFT,
7485   },
7486   {
7487     SPR_push + 0,                       2,
7488     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_UP,
7489   },
7490   {
7491     SPR_push + 1,                       2,
7492     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_RIGHT,
7493   },
7494   {
7495     SPR_push + 2,                       2,
7496     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_DOWN,
7497   },
7498   {
7499     SPR_push + 3,                       2,
7500     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_LEFT,
7501   },
7502   {
7503     SPR_spray + 0,                      2,
7504     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_UP,
7505   },
7506   {
7507     SPR_spray + 1,                      2,
7508     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7509   },
7510   {
7511     SPR_spray + 2,                      2,
7512     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_DOWN,
7513   },
7514   {
7515     SPR_spray + 3,                      2,
7516     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_LEFT,
7517   },
7518   {
7519     SPR_walk + 0,                       3,
7520     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_UP,
7521   },
7522   {
7523     SPR_walk + 1,                       3,
7524     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_RIGHT,
7525   },
7526   {
7527     SPR_walk + 2,                       3,
7528     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_DOWN,
7529   },
7530   {
7531     SPR_walk + 3,                       3,
7532     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_LEFT,
7533   },
7534   {
7535     SPR_push + 0,                       3,
7536     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_UP,
7537   },
7538   {
7539     SPR_push + 1,                       3,
7540     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_RIGHT,
7541   },
7542   {
7543     SPR_push + 2,                       3,
7544     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_DOWN,
7545   },
7546   {
7547     SPR_push + 3,                       3,
7548     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_LEFT,
7549   },
7550   {
7551     SPR_spray + 0,                      3,
7552     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_UP,
7553   },
7554   {
7555     SPR_spray + 1,                      3,
7556     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7557   },
7558   {
7559     SPR_spray + 2,                      3,
7560     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_DOWN,
7561   },
7562   {
7563     SPR_spray + 3,                      3,
7564     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_LEFT,
7565   },
7566   {
7567     SPR_still,                          2,
7568     EL_PLAYER_3,                        ACTION_DEFAULT, -1,
7569   },
7570   {
7571     SPR_still,                          3,
7572     EL_PLAYER_4,                        ACTION_DEFAULT, -1,
7573   },
7574
7575   {
7576     -1,                                 -1,
7577     -1,                                 -1, -1
7578   }
7579 };
7580
7581 int map_element_RND_to_EM(int element_rnd)
7582 {
7583   static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7584   static boolean mapping_initialized = FALSE;
7585
7586   if (!mapping_initialized)
7587   {
7588     int i;
7589
7590     /* return "Xalpha_quest" for all undefined elements in mapping array */
7591     for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7592       mapping_RND_to_EM[i] = Xalpha_quest;
7593
7594     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7595       if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7596         mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7597           em_object_mapping_list[i].element_em;
7598
7599     mapping_initialized = TRUE;
7600   }
7601
7602   if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7603     return mapping_RND_to_EM[element_rnd];
7604
7605   Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7606
7607   return EL_UNKNOWN;
7608 }
7609
7610 int map_element_EM_to_RND(int element_em)
7611 {
7612   static unsigned short mapping_EM_to_RND[TILE_MAX];
7613   static boolean mapping_initialized = FALSE;
7614
7615   if (!mapping_initialized)
7616   {
7617     int i;
7618
7619     /* return "EL_UNKNOWN" for all undefined elements in mapping array */
7620     for (i = 0; i < TILE_MAX; i++)
7621       mapping_EM_to_RND[i] = EL_UNKNOWN;
7622
7623     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7624       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7625         em_object_mapping_list[i].element_rnd;
7626
7627     mapping_initialized = TRUE;
7628   }
7629
7630   if (element_em >= 0 && element_em < TILE_MAX)
7631     return mapping_EM_to_RND[element_em];
7632
7633   Error(ERR_WARN, "invalid EM level element %d", element_em);
7634
7635   return EL_UNKNOWN;
7636 }
7637
7638 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7639 {
7640   struct LevelInfo_EM *level_em = level->native_em_level;
7641   struct LEVEL *lev = level_em->lev;
7642   int i, j;
7643
7644   for (i = 0; i < TILE_MAX; i++)
7645     lev->android_array[i] = Xblank;
7646
7647   for (i = 0; i < level->num_android_clone_elements; i++)
7648   {
7649     int element_rnd = level->android_clone_element[i];
7650     int element_em = map_element_RND_to_EM(element_rnd);
7651
7652     for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7653       if (em_object_mapping_list[j].element_rnd == element_rnd)
7654         lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7655   }
7656 }
7657
7658 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7659 {
7660   struct LevelInfo_EM *level_em = level->native_em_level;
7661   struct LEVEL *lev = level_em->lev;
7662   int i, j;
7663
7664   level->num_android_clone_elements = 0;
7665
7666   for (i = 0; i < TILE_MAX; i++)
7667   {
7668     int element_em = lev->android_array[i];
7669     int element_rnd;
7670     boolean element_found = FALSE;
7671
7672     if (element_em == Xblank)
7673       continue;
7674
7675     element_rnd = map_element_EM_to_RND(element_em);
7676
7677     for (j = 0; j < level->num_android_clone_elements; j++)
7678       if (level->android_clone_element[j] == element_rnd)
7679         element_found = TRUE;
7680
7681     if (!element_found)
7682     {
7683       level->android_clone_element[level->num_android_clone_elements++] =
7684         element_rnd;
7685
7686       if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7687         break;
7688     }
7689   }
7690
7691   if (level->num_android_clone_elements == 0)
7692   {
7693     level->num_android_clone_elements = 1;
7694     level->android_clone_element[0] = EL_EMPTY;
7695   }
7696 }
7697
7698 int map_direction_RND_to_EM(int direction)
7699 {
7700   return (direction == MV_UP    ? 0 :
7701           direction == MV_RIGHT ? 1 :
7702           direction == MV_DOWN  ? 2 :
7703           direction == MV_LEFT  ? 3 :
7704           -1);
7705 }
7706
7707 int map_direction_EM_to_RND(int direction)
7708 {
7709   return (direction == 0 ? MV_UP    :
7710           direction == 1 ? MV_RIGHT :
7711           direction == 2 ? MV_DOWN  :
7712           direction == 3 ? MV_LEFT  :
7713           MV_NONE);
7714 }
7715
7716 int map_element_RND_to_SP(int element_rnd)
7717 {
7718   int element_sp = 0x20;        /* map unknown elements to yellow "hardware" */
7719
7720   if (element_rnd >= EL_SP_START &&
7721       element_rnd <= EL_SP_END)
7722     element_sp = element_rnd - EL_SP_START;
7723   else if (element_rnd == EL_EMPTY_SPACE)
7724     element_sp = 0x00;
7725   else if (element_rnd == EL_INVISIBLE_WALL)
7726     element_sp = 0x28;
7727
7728   return element_sp;
7729 }
7730
7731 int map_element_SP_to_RND(int element_sp)
7732 {
7733   int element_rnd = EL_UNKNOWN;
7734
7735   if (element_sp >= 0x00 &&
7736       element_sp <= 0x27)
7737     element_rnd = EL_SP_START + element_sp;
7738   else if (element_sp == 0x28)
7739     element_rnd = EL_INVISIBLE_WALL;
7740
7741   return element_rnd;
7742 }
7743
7744 int map_action_SP_to_RND(int action_sp)
7745 {
7746   switch (action_sp)
7747   {
7748     case actActive:             return ACTION_ACTIVE;
7749     case actImpact:             return ACTION_IMPACT;
7750     case actExploding:          return ACTION_EXPLODING;
7751     case actDigging:            return ACTION_DIGGING;
7752     case actSnapping:           return ACTION_SNAPPING;
7753     case actCollecting:         return ACTION_COLLECTING;
7754     case actPassing:            return ACTION_PASSING;
7755     case actPushing:            return ACTION_PUSHING;
7756     case actDropping:           return ACTION_DROPPING;
7757
7758     default:                    return ACTION_DEFAULT;
7759   }
7760 }
7761
7762 int map_element_RND_to_MM(int element_rnd)
7763 {
7764   return (element_rnd >= EL_MM_START_1 &&
7765           element_rnd <= EL_MM_END_1 ?
7766           EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7767
7768           element_rnd >= EL_MM_START_2 &&
7769           element_rnd <= EL_MM_END_2 ?
7770           EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7771
7772           element_rnd >= EL_CHAR_START &&
7773           element_rnd <= EL_CHAR_END ?
7774           EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7775
7776           element_rnd >= EL_MM_RUNTIME_START &&
7777           element_rnd <= EL_MM_RUNTIME_END ?
7778           EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7779
7780           element_rnd >= EL_MM_DUMMY_START &&
7781           element_rnd <= EL_MM_DUMMY_END ?
7782           EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7783
7784           EL_MM_EMPTY_NATIVE);
7785 }
7786
7787 int map_element_MM_to_RND(int element_mm)
7788 {
7789   return (element_mm == EL_MM_EMPTY_NATIVE ||
7790           element_mm == EL_DF_EMPTY_NATIVE ?
7791           EL_EMPTY :
7792
7793           element_mm >= EL_MM_START_1_NATIVE &&
7794           element_mm <= EL_MM_END_1_NATIVE ?
7795           EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7796
7797           element_mm >= EL_MM_START_2_NATIVE &&
7798           element_mm <= EL_MM_END_2_NATIVE ?
7799           EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7800
7801           element_mm >= EL_MM_CHAR_START_NATIVE &&
7802           element_mm <= EL_MM_CHAR_END_NATIVE ?
7803           EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7804
7805           element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7806           element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7807           EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7808
7809           element_mm >= EL_MM_DUMMY_START_NATIVE &&
7810           element_mm <= EL_MM_DUMMY_END_NATIVE ?
7811           EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7812
7813           EL_EMPTY);
7814 }
7815
7816 int map_action_MM_to_RND(int action_mm)
7817 {
7818   /* all MM actions are defined to exactly match their RND counterparts */
7819   return action_mm;
7820 }
7821
7822 int map_sound_MM_to_RND(int sound_mm)
7823 {
7824   switch (sound_mm)
7825   {
7826     case SND_MM_GAME_LEVELTIME_CHARGING:
7827       return SND_GAME_LEVELTIME_CHARGING;
7828
7829     case SND_MM_GAME_HEALTH_CHARGING:
7830       return SND_GAME_HEALTH_CHARGING;
7831
7832     default:
7833       return SND_UNDEFINED;
7834   }
7835 }
7836
7837 int map_mm_wall_element(int element)
7838 {
7839   return (element >= EL_MM_STEEL_WALL_START &&
7840           element <= EL_MM_STEEL_WALL_END ?
7841           EL_MM_STEEL_WALL :
7842
7843           element >= EL_MM_WOODEN_WALL_START &&
7844           element <= EL_MM_WOODEN_WALL_END ?
7845           EL_MM_WOODEN_WALL :
7846
7847           element >= EL_MM_ICE_WALL_START &&
7848           element <= EL_MM_ICE_WALL_END ?
7849           EL_MM_ICE_WALL :
7850
7851           element >= EL_MM_AMOEBA_WALL_START &&
7852           element <= EL_MM_AMOEBA_WALL_END ?
7853           EL_MM_AMOEBA_WALL :
7854
7855           element >= EL_DF_STEEL_WALL_START &&
7856           element <= EL_DF_STEEL_WALL_END ?
7857           EL_DF_STEEL_WALL :
7858
7859           element >= EL_DF_WOODEN_WALL_START &&
7860           element <= EL_DF_WOODEN_WALL_END ?
7861           EL_DF_WOODEN_WALL :
7862
7863           element);
7864 }
7865
7866 int map_mm_wall_element_editor(int element)
7867 {
7868   switch (element)
7869   {
7870     case EL_MM_STEEL_WALL:      return EL_MM_STEEL_WALL_START;
7871     case EL_MM_WOODEN_WALL:     return EL_MM_WOODEN_WALL_START;
7872     case EL_MM_ICE_WALL:        return EL_MM_ICE_WALL_START;
7873     case EL_MM_AMOEBA_WALL:     return EL_MM_AMOEBA_WALL_START;
7874     case EL_DF_STEEL_WALL:      return EL_DF_STEEL_WALL_START;
7875     case EL_DF_WOODEN_WALL:     return EL_DF_WOODEN_WALL_START;
7876
7877     default:                    return element;
7878   }
7879 }
7880
7881 int get_next_element(int element)
7882 {
7883   switch (element)
7884   {
7885     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
7886     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
7887     case EL_QUICKSAND_FAST_FILLING:     return EL_QUICKSAND_FAST_FULL;
7888     case EL_QUICKSAND_FAST_EMPTYING:    return EL_QUICKSAND_FAST_EMPTY;
7889     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
7890     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
7891     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
7892     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
7893     case EL_DC_MAGIC_WALL_FILLING:      return EL_DC_MAGIC_WALL_FULL;
7894     case EL_DC_MAGIC_WALL_EMPTYING:     return EL_DC_MAGIC_WALL_ACTIVE;
7895     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
7896
7897     default:                            return element;
7898   }
7899 }
7900
7901 int el2img_mm(int element_mm)
7902 {
7903   return el2img(map_element_MM_to_RND(element_mm));
7904 }
7905
7906 int el_act_dir2img(int element, int action, int direction)
7907 {
7908   element = GFX_ELEMENT(element);
7909   direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7910
7911   /* direction_graphic[][] == graphic[] for undefined direction graphics */
7912   return element_info[element].direction_graphic[action][direction];
7913 }
7914
7915 static int el_act_dir2crm(int element, int action, int direction)
7916 {
7917   element = GFX_ELEMENT(element);
7918   direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7919
7920   /* direction_graphic[][] == graphic[] for undefined direction graphics */
7921   return element_info[element].direction_crumbled[action][direction];
7922 }
7923
7924 int el_act2img(int element, int action)
7925 {
7926   element = GFX_ELEMENT(element);
7927
7928   return element_info[element].graphic[action];
7929 }
7930
7931 int el_act2crm(int element, int action)
7932 {
7933   element = GFX_ELEMENT(element);
7934
7935   return element_info[element].crumbled[action];
7936 }
7937
7938 int el_dir2img(int element, int direction)
7939 {
7940   element = GFX_ELEMENT(element);
7941
7942   return el_act_dir2img(element, ACTION_DEFAULT, direction);
7943 }
7944
7945 int el2baseimg(int element)
7946 {
7947   return element_info[element].graphic[ACTION_DEFAULT];
7948 }
7949
7950 int el2img(int element)
7951 {
7952   element = GFX_ELEMENT(element);
7953
7954   return element_info[element].graphic[ACTION_DEFAULT];
7955 }
7956
7957 int el2edimg(int element)
7958 {
7959   element = GFX_ELEMENT(element);
7960
7961   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7962 }
7963
7964 int el2preimg(int element)
7965 {
7966   element = GFX_ELEMENT(element);
7967
7968   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7969 }
7970
7971 int el2panelimg(int element)
7972 {
7973   element = GFX_ELEMENT(element);
7974
7975   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7976 }
7977
7978 int font2baseimg(int font_nr)
7979 {
7980   return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7981 }
7982
7983 int getBeltNrFromBeltElement(int element)
7984 {
7985   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7986           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7987           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7988 }
7989
7990 int getBeltNrFromBeltActiveElement(int element)
7991 {
7992   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7993           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7994           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7995 }
7996
7997 int getBeltNrFromBeltSwitchElement(int element)
7998 {
7999   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8000           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8001           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8002 }
8003
8004 int getBeltDirNrFromBeltElement(int element)
8005 {
8006   static int belt_base_element[4] =
8007   {
8008     EL_CONVEYOR_BELT_1_LEFT,
8009     EL_CONVEYOR_BELT_2_LEFT,
8010     EL_CONVEYOR_BELT_3_LEFT,
8011     EL_CONVEYOR_BELT_4_LEFT
8012   };
8013
8014   int belt_nr = getBeltNrFromBeltElement(element);
8015   int belt_dir_nr = element - belt_base_element[belt_nr];
8016
8017   return (belt_dir_nr % 3);
8018 }
8019
8020 int getBeltDirNrFromBeltSwitchElement(int element)
8021 {
8022   static int belt_base_element[4] =
8023   {
8024     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8025     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8026     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8027     EL_CONVEYOR_BELT_4_SWITCH_LEFT
8028   };
8029
8030   int belt_nr = getBeltNrFromBeltSwitchElement(element);
8031   int belt_dir_nr = element - belt_base_element[belt_nr];
8032
8033   return (belt_dir_nr % 3);
8034 }
8035
8036 int getBeltDirFromBeltElement(int element)
8037 {
8038   static int belt_move_dir[3] =
8039   {
8040     MV_LEFT,
8041     MV_NONE,
8042     MV_RIGHT
8043   };
8044
8045   int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8046
8047   return belt_move_dir[belt_dir_nr];
8048 }
8049
8050 int getBeltDirFromBeltSwitchElement(int element)
8051 {
8052   static int belt_move_dir[3] =
8053   {
8054     MV_LEFT,
8055     MV_NONE,
8056     MV_RIGHT
8057   };
8058
8059   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8060
8061   return belt_move_dir[belt_dir_nr];
8062 }
8063
8064 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8065 {
8066   static int belt_base_element[4] =
8067   {
8068     EL_CONVEYOR_BELT_1_LEFT,
8069     EL_CONVEYOR_BELT_2_LEFT,
8070     EL_CONVEYOR_BELT_3_LEFT,
8071     EL_CONVEYOR_BELT_4_LEFT
8072   };
8073
8074   return belt_base_element[belt_nr] + belt_dir_nr;
8075 }
8076
8077 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8078 {
8079   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8080
8081   return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8082 }
8083
8084 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8085 {
8086   static int belt_base_element[4] =
8087   {
8088     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8089     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8090     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8091     EL_CONVEYOR_BELT_4_SWITCH_LEFT
8092   };
8093
8094   return belt_base_element[belt_nr] + belt_dir_nr;
8095 }
8096
8097 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8098 {
8099   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8100
8101   return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8102 }
8103
8104 boolean getTeamMode_EM()
8105 {
8106   return game.team_mode;
8107 }
8108
8109 int getGameFrameDelay_EM(int native_em_game_frame_delay)
8110 {
8111   int game_frame_delay_value;
8112
8113   game_frame_delay_value =
8114     (tape.playing && tape.fast_forward ? FfwdFrameDelay :
8115      GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
8116      GameFrameDelay);
8117
8118   if (tape.playing && tape.warp_forward && !tape.pausing)
8119     game_frame_delay_value = 0;
8120
8121   return game_frame_delay_value;
8122 }
8123
8124 unsigned int InitRND(int seed)
8125 {
8126   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8127     return InitEngineRandom_EM(seed);
8128   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8129     return InitEngineRandom_SP(seed);
8130   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8131     return InitEngineRandom_MM(seed);
8132   else
8133     return InitEngineRandom_RND(seed);
8134 }
8135
8136 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
8137 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
8138
8139 inline static int get_effective_element_EM(int tile, int frame_em)
8140 {
8141   int element             = object_mapping[tile].element_rnd;
8142   int action              = object_mapping[tile].action;
8143   boolean is_backside     = object_mapping[tile].is_backside;
8144   boolean action_removing = (action == ACTION_DIGGING ||
8145                              action == ACTION_SNAPPING ||
8146                              action == ACTION_COLLECTING);
8147
8148   if (frame_em < 7)
8149   {
8150     switch (tile)
8151     {
8152       case Yacid_splash_eB:
8153       case Yacid_splash_wB:
8154         return (frame_em > 5 ? EL_EMPTY : element);
8155
8156       default:
8157         return element;
8158     }
8159   }
8160   else  /* frame_em == 7 */
8161   {
8162     switch (tile)
8163     {
8164       case Yacid_splash_eB:
8165       case Yacid_splash_wB:
8166         return EL_EMPTY;
8167
8168       case Yemerald_stone:
8169         return EL_EMERALD;
8170
8171       case Ydiamond_stone:
8172         return EL_ROCK;
8173
8174       case Xdrip_stretch:
8175       case Xdrip_stretchB:
8176       case Ydrip_s1:
8177       case Ydrip_s1B:
8178       case Xball_1B:
8179       case Xball_2:
8180       case Xball_2B:
8181       case Yball_eat:
8182       case Ykey_1_eat:
8183       case Ykey_2_eat:
8184       case Ykey_3_eat:
8185       case Ykey_4_eat:
8186       case Ykey_5_eat:
8187       case Ykey_6_eat:
8188       case Ykey_7_eat:
8189       case Ykey_8_eat:
8190       case Ylenses_eat:
8191       case Ymagnify_eat:
8192       case Ygrass_eat:
8193       case Ydirt_eat:
8194       case Xsand_stonein_1:
8195       case Xsand_stonein_2:
8196       case Xsand_stonein_3:
8197       case Xsand_stonein_4:
8198         return element;
8199
8200       default:
8201         return (is_backside || action_removing ? EL_EMPTY : element);
8202     }
8203   }
8204 }
8205
8206 inline static boolean check_linear_animation_EM(int tile)
8207 {
8208   switch (tile)
8209   {
8210     case Xsand_stonesand_1:
8211     case Xsand_stonesand_quickout_1:
8212     case Xsand_sandstone_1:
8213     case Xsand_stonein_1:
8214     case Xsand_stoneout_1:
8215     case Xboom_1:
8216     case Xdynamite_1:
8217     case Ybug_w_n:
8218     case Ybug_n_e:
8219     case Ybug_e_s:
8220     case Ybug_s_w:
8221     case Ybug_e_n:
8222     case Ybug_s_e:
8223     case Ybug_w_s:
8224     case Ybug_n_w:
8225     case Ytank_w_n:
8226     case Ytank_n_e:
8227     case Ytank_e_s:
8228     case Ytank_s_w:
8229     case Ytank_e_n:
8230     case Ytank_s_e:
8231     case Ytank_w_s:
8232     case Ytank_n_w:
8233     case Yacid_splash_eB:
8234     case Yacid_splash_wB:
8235     case Yemerald_stone:
8236       return TRUE;
8237   }
8238
8239   return FALSE;
8240 }
8241
8242 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8243                                             boolean has_crumbled_graphics,
8244                                             int crumbled, int sync_frame)
8245 {
8246   /* if element can be crumbled, but certain action graphics are just empty
8247      space (like instantly snapping sand to empty space in 1 frame), do not
8248      treat these empty space graphics as crumbled graphics in EMC engine */
8249   if (crumbled == IMG_EMPTY_SPACE)
8250     has_crumbled_graphics = FALSE;
8251
8252   if (has_crumbled_graphics)
8253   {
8254     struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8255     int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8256                                            g_crumbled->anim_delay,
8257                                            g_crumbled->anim_mode,
8258                                            g_crumbled->anim_start_frame,
8259                                            sync_frame);
8260
8261     getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8262                      &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8263
8264     g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8265     g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8266
8267     g_em->has_crumbled_graphics = TRUE;
8268   }
8269   else
8270   {
8271     g_em->crumbled_bitmap = NULL;
8272     g_em->crumbled_src_x = 0;
8273     g_em->crumbled_src_y = 0;
8274     g_em->crumbled_border_size = 0;
8275     g_em->crumbled_tile_size = 0;
8276
8277     g_em->has_crumbled_graphics = FALSE;
8278   }
8279 }
8280
8281 void ResetGfxAnimation_EM(int x, int y, int tile)
8282 {
8283   GfxFrame[x][y] = 0;
8284 }
8285
8286 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8287                         int tile, int frame_em, int x, int y)
8288 {
8289   int action = object_mapping[tile].action;
8290   int direction = object_mapping[tile].direction;
8291   int effective_element = get_effective_element_EM(tile, frame_em);
8292   int graphic = (direction == MV_NONE ?
8293                  el_act2img(effective_element, action) :
8294                  el_act_dir2img(effective_element, action, direction));
8295   struct GraphicInfo *g = &graphic_info[graphic];
8296   int sync_frame;
8297   boolean action_removing = (action == ACTION_DIGGING ||
8298                              action == ACTION_SNAPPING ||
8299                              action == ACTION_COLLECTING);
8300   boolean action_moving   = (action == ACTION_FALLING ||
8301                              action == ACTION_MOVING ||
8302                              action == ACTION_PUSHING ||
8303                              action == ACTION_EATING ||
8304                              action == ACTION_FILLING ||
8305                              action == ACTION_EMPTYING);
8306   boolean action_falling  = (action == ACTION_FALLING ||
8307                              action == ACTION_FILLING ||
8308                              action == ACTION_EMPTYING);
8309
8310   /* special case: graphic uses "2nd movement tile" and has defined
8311      7 frames for movement animation (or less) => use default graphic
8312      for last (8th) frame which ends the movement animation */
8313   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8314   {
8315     action = ACTION_DEFAULT;    /* (keep action_* unchanged for now) */
8316     graphic = (direction == MV_NONE ?
8317                el_act2img(effective_element, action) :
8318                el_act_dir2img(effective_element, action, direction));
8319
8320     g = &graphic_info[graphic];
8321   }
8322
8323   if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8324   {
8325     GfxFrame[x][y] = 0;
8326   }
8327   else if (action_moving)
8328   {
8329     boolean is_backside = object_mapping[tile].is_backside;
8330
8331     if (is_backside)
8332     {
8333       int direction = object_mapping[tile].direction;
8334       int move_dir = (action_falling ? MV_DOWN : direction);
8335
8336       GfxFrame[x][y]++;
8337
8338 #if 1
8339       /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
8340       if (g->double_movement && frame_em == 0)
8341         GfxFrame[x][y] = 0;
8342 #endif
8343
8344       if (move_dir == MV_LEFT)
8345         GfxFrame[x - 1][y] = GfxFrame[x][y];
8346       else if (move_dir == MV_RIGHT)
8347         GfxFrame[x + 1][y] = GfxFrame[x][y];
8348       else if (move_dir == MV_UP)
8349         GfxFrame[x][y - 1] = GfxFrame[x][y];
8350       else if (move_dir == MV_DOWN)
8351         GfxFrame[x][y + 1] = GfxFrame[x][y];
8352     }
8353   }
8354   else
8355   {
8356     GfxFrame[x][y]++;
8357
8358     /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
8359     if (tile == Xsand_stonesand_quickout_1 ||
8360         tile == Xsand_stonesand_quickout_2)
8361       GfxFrame[x][y]++;
8362   }
8363
8364   if (graphic_info[graphic].anim_global_sync)
8365     sync_frame = FrameCounter;
8366   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8367     sync_frame = GfxFrame[x][y];
8368   else
8369     sync_frame = 0;     /* playfield border (pseudo steel) */
8370
8371   SetRandomAnimationValue(x, y);
8372
8373   int frame = getAnimationFrame(g->anim_frames,
8374                                 g->anim_delay,
8375                                 g->anim_mode,
8376                                 g->anim_start_frame,
8377                                 sync_frame);
8378
8379   g_em->unique_identifier =
8380     (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8381 }
8382
8383 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8384                                   int tile, int frame_em, int x, int y)
8385 {
8386   int action = object_mapping[tile].action;
8387   int direction = object_mapping[tile].direction;
8388   boolean is_backside = object_mapping[tile].is_backside;
8389   int effective_element = get_effective_element_EM(tile, frame_em);
8390   int effective_action = action;
8391   int graphic = (direction == MV_NONE ?
8392                  el_act2img(effective_element, effective_action) :
8393                  el_act_dir2img(effective_element, effective_action,
8394                                 direction));
8395   int crumbled = (direction == MV_NONE ?
8396                   el_act2crm(effective_element, effective_action) :
8397                   el_act_dir2crm(effective_element, effective_action,
8398                                  direction));
8399   int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8400   int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8401   boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8402   struct GraphicInfo *g = &graphic_info[graphic];
8403   int sync_frame;
8404
8405   /* special case: graphic uses "2nd movement tile" and has defined
8406      7 frames for movement animation (or less) => use default graphic
8407      for last (8th) frame which ends the movement animation */
8408   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8409   {
8410     effective_action = ACTION_DEFAULT;
8411     graphic = (direction == MV_NONE ?
8412                el_act2img(effective_element, effective_action) :
8413                el_act_dir2img(effective_element, effective_action,
8414                               direction));
8415     crumbled = (direction == MV_NONE ?
8416                 el_act2crm(effective_element, effective_action) :
8417                 el_act_dir2crm(effective_element, effective_action,
8418                                direction));
8419
8420     g = &graphic_info[graphic];
8421   }
8422
8423   if (graphic_info[graphic].anim_global_sync)
8424     sync_frame = FrameCounter;
8425   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8426     sync_frame = GfxFrame[x][y];
8427   else
8428     sync_frame = 0;     /* playfield border (pseudo steel) */
8429
8430   SetRandomAnimationValue(x, y);
8431
8432   int frame = getAnimationFrame(g->anim_frames,
8433                                 g->anim_delay,
8434                                 g->anim_mode,
8435                                 g->anim_start_frame,
8436                                 sync_frame);
8437
8438   getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8439                       g->double_movement && is_backside);
8440
8441   /* (updating the "crumbled" graphic definitions is probably not really needed,
8442      as animations for crumbled graphics can't be longer than one EMC cycle) */
8443   set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8444                            sync_frame);
8445 }
8446
8447 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8448                                   int player_nr, int anim, int frame_em)
8449 {
8450   int element   = player_mapping[player_nr][anim].element_rnd;
8451   int action    = player_mapping[player_nr][anim].action;
8452   int direction = player_mapping[player_nr][anim].direction;
8453   int graphic = (direction == MV_NONE ?
8454                  el_act2img(element, action) :
8455                  el_act_dir2img(element, action, direction));
8456   struct GraphicInfo *g = &graphic_info[graphic];
8457   int sync_frame;
8458
8459   InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8460
8461   stored_player[player_nr].StepFrame = frame_em;
8462
8463   sync_frame = stored_player[player_nr].Frame;
8464
8465   int frame = getAnimationFrame(g->anim_frames,
8466                                 g->anim_delay,
8467                                 g->anim_mode,
8468                                 g->anim_start_frame,
8469                                 sync_frame);
8470
8471   getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8472                       &g_em->src_x, &g_em->src_y, FALSE);
8473 }
8474
8475 void InitGraphicInfo_EM(void)
8476 {
8477   int i, j, p;
8478
8479 #if DEBUG_EM_GFX
8480   int num_em_gfx_errors = 0;
8481
8482   if (graphic_info_em_object[0][0].bitmap == NULL)
8483   {
8484     /* EM graphics not yet initialized in em_open_all() */
8485
8486     return;
8487   }
8488
8489   printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8490 #endif
8491
8492   /* always start with reliable default values */
8493   for (i = 0; i < TILE_MAX; i++)
8494   {
8495     object_mapping[i].element_rnd = EL_UNKNOWN;
8496     object_mapping[i].is_backside = FALSE;
8497     object_mapping[i].action = ACTION_DEFAULT;
8498     object_mapping[i].direction = MV_NONE;
8499   }
8500
8501   /* always start with reliable default values */
8502   for (p = 0; p < MAX_PLAYERS; p++)
8503   {
8504     for (i = 0; i < SPR_MAX; i++)
8505     {
8506       player_mapping[p][i].element_rnd = EL_UNKNOWN;
8507       player_mapping[p][i].action = ACTION_DEFAULT;
8508       player_mapping[p][i].direction = MV_NONE;
8509     }
8510   }
8511
8512   for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8513   {
8514     int e = em_object_mapping_list[i].element_em;
8515
8516     object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8517     object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8518
8519     if (em_object_mapping_list[i].action != -1)
8520       object_mapping[e].action = em_object_mapping_list[i].action;
8521
8522     if (em_object_mapping_list[i].direction != -1)
8523       object_mapping[e].direction =
8524         MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8525   }
8526
8527   for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8528   {
8529     int a = em_player_mapping_list[i].action_em;
8530     int p = em_player_mapping_list[i].player_nr;
8531
8532     player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8533
8534     if (em_player_mapping_list[i].action != -1)
8535       player_mapping[p][a].action = em_player_mapping_list[i].action;
8536
8537     if (em_player_mapping_list[i].direction != -1)
8538       player_mapping[p][a].direction =
8539         MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8540   }
8541
8542   for (i = 0; i < TILE_MAX; i++)
8543   {
8544     int element = object_mapping[i].element_rnd;
8545     int action = object_mapping[i].action;
8546     int direction = object_mapping[i].direction;
8547     boolean is_backside = object_mapping[i].is_backside;
8548     boolean action_exploding = ((action == ACTION_EXPLODING ||
8549                                  action == ACTION_SMASHED_BY_ROCK ||
8550                                  action == ACTION_SMASHED_BY_SPRING) &&
8551                                 element != EL_DIAMOND);
8552     boolean action_active = (action == ACTION_ACTIVE);
8553     boolean action_other = (action == ACTION_OTHER);
8554
8555     for (j = 0; j < 8; j++)
8556     {
8557       int effective_element = get_effective_element_EM(i, j);
8558       int effective_action = (j < 7 ? action :
8559                               i == Xdrip_stretch ? action :
8560                               i == Xdrip_stretchB ? action :
8561                               i == Ydrip_s1 ? action :
8562                               i == Ydrip_s1B ? action :
8563                               i == Xball_1B ? action :
8564                               i == Xball_2 ? action :
8565                               i == Xball_2B ? action :
8566                               i == Yball_eat ? action :
8567                               i == Ykey_1_eat ? action :
8568                               i == Ykey_2_eat ? action :
8569                               i == Ykey_3_eat ? action :
8570                               i == Ykey_4_eat ? action :
8571                               i == Ykey_5_eat ? action :
8572                               i == Ykey_6_eat ? action :
8573                               i == Ykey_7_eat ? action :
8574                               i == Ykey_8_eat ? action :
8575                               i == Ylenses_eat ? action :
8576                               i == Ymagnify_eat ? action :
8577                               i == Ygrass_eat ? action :
8578                               i == Ydirt_eat ? action :
8579                               i == Xsand_stonein_1 ? action :
8580                               i == Xsand_stonein_2 ? action :
8581                               i == Xsand_stonein_3 ? action :
8582                               i == Xsand_stonein_4 ? action :
8583                               i == Xsand_stoneout_1 ? action :
8584                               i == Xsand_stoneout_2 ? action :
8585                               i == Xboom_android ? ACTION_EXPLODING :
8586                               action_exploding ? ACTION_EXPLODING :
8587                               action_active ? action :
8588                               action_other ? action :
8589                               ACTION_DEFAULT);
8590       int graphic = (el_act_dir2img(effective_element, effective_action,
8591                                     direction));
8592       int crumbled = (el_act_dir2crm(effective_element, effective_action,
8593                                      direction));
8594       int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8595       int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8596       boolean has_action_graphics = (graphic != base_graphic);
8597       boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8598       struct GraphicInfo *g = &graphic_info[graphic];
8599       struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8600       Bitmap *src_bitmap;
8601       int src_x, src_y;
8602       /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
8603       boolean special_animation = (action != ACTION_DEFAULT &&
8604                                    g->anim_frames == 3 &&
8605                                    g->anim_delay == 2 &&
8606                                    g->anim_mode & ANIM_LINEAR);
8607       int sync_frame = (i == Xdrip_stretch ? 7 :
8608                         i == Xdrip_stretchB ? 7 :
8609                         i == Ydrip_s2 ? j + 8 :
8610                         i == Ydrip_s2B ? j + 8 :
8611                         i == Xacid_1 ? 0 :
8612                         i == Xacid_2 ? 10 :
8613                         i == Xacid_3 ? 20 :
8614                         i == Xacid_4 ? 30 :
8615                         i == Xacid_5 ? 40 :
8616                         i == Xacid_6 ? 50 :
8617                         i == Xacid_7 ? 60 :
8618                         i == Xacid_8 ? 70 :
8619                         i == Xfake_acid_1 ? 0 :
8620                         i == Xfake_acid_2 ? 10 :
8621                         i == Xfake_acid_3 ? 20 :
8622                         i == Xfake_acid_4 ? 30 :
8623                         i == Xfake_acid_5 ? 40 :
8624                         i == Xfake_acid_6 ? 50 :
8625                         i == Xfake_acid_7 ? 60 :
8626                         i == Xfake_acid_8 ? 70 :
8627                         i == Xball_2 ? 7 :
8628                         i == Xball_2B ? j + 8 :
8629                         i == Yball_eat ? j + 1 :
8630                         i == Ykey_1_eat ? j + 1 :
8631                         i == Ykey_2_eat ? j + 1 :
8632                         i == Ykey_3_eat ? j + 1 :
8633                         i == Ykey_4_eat ? j + 1 :
8634                         i == Ykey_5_eat ? j + 1 :
8635                         i == Ykey_6_eat ? j + 1 :
8636                         i == Ykey_7_eat ? j + 1 :
8637                         i == Ykey_8_eat ? j + 1 :
8638                         i == Ylenses_eat ? j + 1 :
8639                         i == Ymagnify_eat ? j + 1 :
8640                         i == Ygrass_eat ? j + 1 :
8641                         i == Ydirt_eat ? j + 1 :
8642                         i == Xamoeba_1 ? 0 :
8643                         i == Xamoeba_2 ? 1 :
8644                         i == Xamoeba_3 ? 2 :
8645                         i == Xamoeba_4 ? 3 :
8646                         i == Xamoeba_5 ? 0 :
8647                         i == Xamoeba_6 ? 1 :
8648                         i == Xamoeba_7 ? 2 :
8649                         i == Xamoeba_8 ? 3 :
8650                         i == Xexit_2 ? j + 8 :
8651                         i == Xexit_3 ? j + 16 :
8652                         i == Xdynamite_1 ? 0 :
8653                         i == Xdynamite_2 ? 8 :
8654                         i == Xdynamite_3 ? 16 :
8655                         i == Xdynamite_4 ? 24 :
8656                         i == Xsand_stonein_1 ? j + 1 :
8657                         i == Xsand_stonein_2 ? j + 9 :
8658                         i == Xsand_stonein_3 ? j + 17 :
8659                         i == Xsand_stonein_4 ? j + 25 :
8660                         i == Xsand_stoneout_1 && j == 0 ? 0 :
8661                         i == Xsand_stoneout_1 && j == 1 ? 0 :
8662                         i == Xsand_stoneout_1 && j == 2 ? 1 :
8663                         i == Xsand_stoneout_1 && j == 3 ? 2 :
8664                         i == Xsand_stoneout_1 && j == 4 ? 2 :
8665                         i == Xsand_stoneout_1 && j == 5 ? 3 :
8666                         i == Xsand_stoneout_1 && j == 6 ? 4 :
8667                         i == Xsand_stoneout_1 && j == 7 ? 4 :
8668                         i == Xsand_stoneout_2 && j == 0 ? 5 :
8669                         i == Xsand_stoneout_2 && j == 1 ? 6 :
8670                         i == Xsand_stoneout_2 && j == 2 ? 7 :
8671                         i == Xsand_stoneout_2 && j == 3 ? 8 :
8672                         i == Xsand_stoneout_2 && j == 4 ? 9 :
8673                         i == Xsand_stoneout_2 && j == 5 ? 11 :
8674                         i == Xsand_stoneout_2 && j == 6 ? 13 :
8675                         i == Xsand_stoneout_2 && j == 7 ? 15 :
8676                         i == Xboom_bug && j == 1 ? 2 :
8677                         i == Xboom_bug && j == 2 ? 2 :
8678                         i == Xboom_bug && j == 3 ? 4 :
8679                         i == Xboom_bug && j == 4 ? 4 :
8680                         i == Xboom_bug && j == 5 ? 2 :
8681                         i == Xboom_bug && j == 6 ? 2 :
8682                         i == Xboom_bug && j == 7 ? 0 :
8683                         i == Xboom_bomb && j == 1 ? 2 :
8684                         i == Xboom_bomb && j == 2 ? 2 :
8685                         i == Xboom_bomb && j == 3 ? 4 :
8686                         i == Xboom_bomb && j == 4 ? 4 :
8687                         i == Xboom_bomb && j == 5 ? 2 :
8688                         i == Xboom_bomb && j == 6 ? 2 :
8689                         i == Xboom_bomb && j == 7 ? 0 :
8690                         i == Xboom_android && j == 7 ? 6 :
8691                         i == Xboom_1 && j == 1 ? 2 :
8692                         i == Xboom_1 && j == 2 ? 2 :
8693                         i == Xboom_1 && j == 3 ? 4 :
8694                         i == Xboom_1 && j == 4 ? 4 :
8695                         i == Xboom_1 && j == 5 ? 6 :
8696                         i == Xboom_1 && j == 6 ? 6 :
8697                         i == Xboom_1 && j == 7 ? 8 :
8698                         i == Xboom_2 && j == 0 ? 8 :
8699                         i == Xboom_2 && j == 1 ? 8 :
8700                         i == Xboom_2 && j == 2 ? 10 :
8701                         i == Xboom_2 && j == 3 ? 10 :
8702                         i == Xboom_2 && j == 4 ? 10 :
8703                         i == Xboom_2 && j == 5 ? 12 :
8704                         i == Xboom_2 && j == 6 ? 12 :
8705                         i == Xboom_2 && j == 7 ? 12 :
8706                         special_animation && j == 4 ? 3 :
8707                         effective_action != action ? 0 :
8708                         j);
8709
8710 #if DEBUG_EM_GFX
8711       Bitmap *debug_bitmap = g_em->bitmap;
8712       int debug_src_x = g_em->src_x;
8713       int debug_src_y = g_em->src_y;
8714 #endif
8715
8716       int frame = getAnimationFrame(g->anim_frames,
8717                                     g->anim_delay,
8718                                     g->anim_mode,
8719                                     g->anim_start_frame,
8720                                     sync_frame);
8721
8722       getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8723                           g->double_movement && is_backside);
8724
8725       g_em->bitmap = src_bitmap;
8726       g_em->src_x = src_x;
8727       g_em->src_y = src_y;
8728       g_em->src_offset_x = 0;
8729       g_em->src_offset_y = 0;
8730       g_em->dst_offset_x = 0;
8731       g_em->dst_offset_y = 0;
8732       g_em->width  = TILEX;
8733       g_em->height = TILEY;
8734
8735       g_em->preserve_background = FALSE;
8736
8737       set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8738                                sync_frame);
8739
8740       if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8741                                    effective_action == ACTION_MOVING  ||
8742                                    effective_action == ACTION_PUSHING ||
8743                                    effective_action == ACTION_EATING)) ||
8744           (!has_action_graphics && (effective_action == ACTION_FILLING ||
8745                                     effective_action == ACTION_EMPTYING)))
8746       {
8747         int move_dir =
8748           (effective_action == ACTION_FALLING ||
8749            effective_action == ACTION_FILLING ||
8750            effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8751         int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8752         int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? 1 : 0);
8753         int num_steps = (i == Ydrip_s1  ? 16 :
8754                          i == Ydrip_s1B ? 16 :
8755                          i == Ydrip_s2  ? 16 :
8756                          i == Ydrip_s2B ? 16 :
8757                          i == Xsand_stonein_1 ? 32 :
8758                          i == Xsand_stonein_2 ? 32 :
8759                          i == Xsand_stonein_3 ? 32 :
8760                          i == Xsand_stonein_4 ? 32 :
8761                          i == Xsand_stoneout_1 ? 16 :
8762                          i == Xsand_stoneout_2 ? 16 : 8);
8763         int cx = ABS(dx) * (TILEX / num_steps);
8764         int cy = ABS(dy) * (TILEY / num_steps);
8765         int step_frame = (i == Ydrip_s2         ? j + 8 :
8766                           i == Ydrip_s2B        ? j + 8 :
8767                           i == Xsand_stonein_2  ? j + 8 :
8768                           i == Xsand_stonein_3  ? j + 16 :
8769                           i == Xsand_stonein_4  ? j + 24 :
8770                           i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8771         int step = (is_backside ? step_frame : num_steps - step_frame);
8772
8773         if (is_backside)        /* tile where movement starts */
8774         {
8775           if (dx < 0 || dy < 0)
8776           {
8777             g_em->src_offset_x = cx * step;
8778             g_em->src_offset_y = cy * step;
8779           }
8780           else
8781           {
8782             g_em->dst_offset_x = cx * step;
8783             g_em->dst_offset_y = cy * step;
8784           }
8785         }
8786         else                    /* tile where movement ends */
8787         {
8788           if (dx < 0 || dy < 0)
8789           {
8790             g_em->dst_offset_x = cx * step;
8791             g_em->dst_offset_y = cy * step;
8792           }
8793           else
8794           {
8795             g_em->src_offset_x = cx * step;
8796             g_em->src_offset_y = cy * step;
8797           }
8798         }
8799
8800         g_em->width  = TILEX - cx * step;
8801         g_em->height = TILEY - cy * step;
8802       }
8803
8804       /* create unique graphic identifier to decide if tile must be redrawn */
8805       /* bit 31 - 16 (16 bit): EM style graphic
8806          bit 15 - 12 ( 4 bit): EM style frame
8807          bit 11 -  6 ( 6 bit): graphic width
8808          bit  5 -  0 ( 6 bit): graphic height */
8809       g_em->unique_identifier =
8810         (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8811
8812 #if DEBUG_EM_GFX
8813
8814       /* skip check for EMC elements not contained in original EMC artwork */
8815       if (element == EL_EMC_FAKE_ACID)
8816         continue;
8817
8818       if (g_em->bitmap != debug_bitmap ||
8819           g_em->src_x != debug_src_x ||
8820           g_em->src_y != debug_src_y ||
8821           g_em->src_offset_x != 0 ||
8822           g_em->src_offset_y != 0 ||
8823           g_em->dst_offset_x != 0 ||
8824           g_em->dst_offset_y != 0 ||
8825           g_em->width != TILEX ||
8826           g_em->height != TILEY)
8827       {
8828         static int last_i = -1;
8829
8830         if (i != last_i)
8831         {
8832           printf("\n");
8833           last_i = i;
8834         }
8835
8836         printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8837                i, element, element_info[element].token_name,
8838                element_action_info[effective_action].suffix, direction);
8839
8840         if (element != effective_element)
8841           printf(" [%d ('%s')]",
8842                  effective_element,
8843                  element_info[effective_element].token_name);
8844
8845         printf("\n");
8846
8847         if (g_em->bitmap != debug_bitmap)
8848           printf("    %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8849                  j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8850
8851         if (g_em->src_x != debug_src_x ||
8852             g_em->src_y != debug_src_y)
8853           printf("    frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8854                  j, (is_backside ? 'B' : 'F'),
8855                  g_em->src_x, g_em->src_y,
8856                  g_em->src_x / 32, g_em->src_y / 32,
8857                  debug_src_x, debug_src_y,
8858                  debug_src_x / 32, debug_src_y / 32);
8859
8860         if (g_em->src_offset_x != 0 ||
8861             g_em->src_offset_y != 0 ||
8862             g_em->dst_offset_x != 0 ||
8863             g_em->dst_offset_y != 0)
8864           printf("    %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8865                  j, is_backside,
8866                  g_em->src_offset_x, g_em->src_offset_y,
8867                  g_em->dst_offset_x, g_em->dst_offset_y);
8868
8869         if (g_em->width != TILEX ||
8870             g_em->height != TILEY)
8871           printf("    %d (%d): size %d,%d should be %d,%d\n",
8872                  j, is_backside,
8873                  g_em->width, g_em->height, TILEX, TILEY);
8874
8875         num_em_gfx_errors++;
8876       }
8877 #endif
8878
8879     }
8880   }
8881
8882   for (i = 0; i < TILE_MAX; i++)
8883   {
8884     for (j = 0; j < 8; j++)
8885     {
8886       int element = object_mapping[i].element_rnd;
8887       int action = object_mapping[i].action;
8888       int direction = object_mapping[i].direction;
8889       boolean is_backside = object_mapping[i].is_backside;
8890       int graphic_action  = el_act_dir2img(element, action, direction);
8891       int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8892
8893       if ((action == ACTION_SMASHED_BY_ROCK ||
8894            action == ACTION_SMASHED_BY_SPRING ||
8895            action == ACTION_EATING) &&
8896           graphic_action == graphic_default)
8897       {
8898         int e = (action == ACTION_SMASHED_BY_ROCK   ? Ystone_s  :
8899                  action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8900                  direction == MV_LEFT  ? (is_backside? Yspring_wB: Yspring_w) :
8901                  direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8902                  Xspring);
8903
8904         /* no separate animation for "smashed by rock" -- use rock instead */
8905         struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8906         struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8907
8908         g_em->bitmap            = g_xx->bitmap;
8909         g_em->src_x             = g_xx->src_x;
8910         g_em->src_y             = g_xx->src_y;
8911         g_em->src_offset_x      = g_xx->src_offset_x;
8912         g_em->src_offset_y      = g_xx->src_offset_y;
8913         g_em->dst_offset_x      = g_xx->dst_offset_x;
8914         g_em->dst_offset_y      = g_xx->dst_offset_y;
8915         g_em->width             = g_xx->width;
8916         g_em->height            = g_xx->height;
8917         g_em->unique_identifier = g_xx->unique_identifier;
8918
8919         if (!is_backside)
8920           g_em->preserve_background = TRUE;
8921       }
8922     }
8923   }
8924
8925   for (p = 0; p < MAX_PLAYERS; p++)
8926   {
8927     for (i = 0; i < SPR_MAX; i++)
8928     {
8929       int element = player_mapping[p][i].element_rnd;
8930       int action = player_mapping[p][i].action;
8931       int direction = player_mapping[p][i].direction;
8932
8933       for (j = 0; j < 8; j++)
8934       {
8935         int effective_element = element;
8936         int effective_action = action;
8937         int graphic = (direction == MV_NONE ?
8938                        el_act2img(effective_element, effective_action) :
8939                        el_act_dir2img(effective_element, effective_action,
8940                                       direction));
8941         struct GraphicInfo *g = &graphic_info[graphic];
8942         struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8943         Bitmap *src_bitmap;
8944         int src_x, src_y;
8945         int sync_frame = j;
8946
8947 #if DEBUG_EM_GFX
8948         Bitmap *debug_bitmap = g_em->bitmap;
8949         int debug_src_x = g_em->src_x;
8950         int debug_src_y = g_em->src_y;
8951 #endif
8952
8953         int frame = getAnimationFrame(g->anim_frames,
8954                                       g->anim_delay,
8955                                       g->anim_mode,
8956                                       g->anim_start_frame,
8957                                       sync_frame);
8958
8959         getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8960
8961         g_em->bitmap = src_bitmap;
8962         g_em->src_x = src_x;
8963         g_em->src_y = src_y;
8964         g_em->src_offset_x = 0;
8965         g_em->src_offset_y = 0;
8966         g_em->dst_offset_x = 0;
8967         g_em->dst_offset_y = 0;
8968         g_em->width  = TILEX;
8969         g_em->height = TILEY;
8970
8971 #if DEBUG_EM_GFX
8972
8973         /* skip check for EMC elements not contained in original EMC artwork */
8974         if (element == EL_PLAYER_3 ||
8975             element == EL_PLAYER_4)
8976           continue;
8977
8978         if (g_em->bitmap != debug_bitmap ||
8979             g_em->src_x != debug_src_x ||
8980             g_em->src_y != debug_src_y)
8981         {
8982           static int last_i = -1;
8983
8984           if (i != last_i)
8985           {
8986             printf("\n");
8987             last_i = i;
8988           }
8989
8990           printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8991                  p, i, element, element_info[element].token_name,
8992                  element_action_info[effective_action].suffix, direction);
8993
8994           if (element != effective_element)
8995             printf(" [%d ('%s')]",
8996                    effective_element,
8997                    element_info[effective_element].token_name);
8998
8999           printf("\n");
9000
9001           if (g_em->bitmap != debug_bitmap)
9002             printf("    %d: different bitmap! (0x%08x != 0x%08x)\n",
9003                    j, (int)(g_em->bitmap), (int)(debug_bitmap));
9004
9005           if (g_em->src_x != debug_src_x ||
9006               g_em->src_y != debug_src_y)
9007             printf("    frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
9008                    j,
9009                    g_em->src_x, g_em->src_y,
9010                    g_em->src_x / 32, g_em->src_y / 32,
9011                    debug_src_x, debug_src_y,
9012                    debug_src_x / 32, debug_src_y / 32);
9013
9014           num_em_gfx_errors++;
9015         }
9016 #endif
9017
9018       }
9019     }
9020   }
9021
9022 #if DEBUG_EM_GFX
9023   printf("\n");
9024   printf("::: [%d errors found]\n", num_em_gfx_errors);
9025
9026   exit(0);
9027 #endif
9028 }
9029
9030 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9031                                 boolean any_player_moving,
9032                                 boolean any_player_snapping,
9033                                 boolean any_player_dropping)
9034 {
9035   if (frame == 0 && !any_player_dropping)
9036   {
9037     if (!local_player->was_waiting)
9038     {
9039       if (!CheckSaveEngineSnapshotToList())
9040         return;
9041
9042       local_player->was_waiting = TRUE;
9043     }
9044   }
9045   else if (any_player_moving || any_player_snapping || any_player_dropping)
9046   {
9047     local_player->was_waiting = FALSE;
9048   }
9049 }
9050
9051 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9052                                 boolean murphy_is_dropping)
9053 {
9054   if (murphy_is_waiting)
9055   {
9056     if (!local_player->was_waiting)
9057     {
9058       if (!CheckSaveEngineSnapshotToList())
9059         return;
9060
9061       local_player->was_waiting = TRUE;
9062     }
9063   }
9064   else
9065   {
9066     local_player->was_waiting = FALSE;
9067   }
9068 }
9069
9070 void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9071                                 boolean button_released)
9072 {
9073   if (button_released)
9074   {
9075     if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9076       CheckSaveEngineSnapshotToList();
9077   }
9078   else if (element_clicked)
9079   {
9080     if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9081       CheckSaveEngineSnapshotToList();
9082
9083     game.snapshot.changed_action = TRUE;
9084   }
9085 }
9086
9087 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9088                             boolean any_player_moving,
9089                             boolean any_player_snapping,
9090                             boolean any_player_dropping)
9091 {
9092   if (tape.single_step && tape.recording && !tape.pausing)
9093     if (frame == 0 && !any_player_dropping)
9094       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9095
9096   CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9097                              any_player_snapping, any_player_dropping);
9098 }
9099
9100 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9101                             boolean murphy_is_dropping)
9102 {
9103   boolean murphy_starts_dropping = FALSE;
9104   int i;
9105
9106   for (i = 0; i < MAX_PLAYERS; i++)
9107     if (stored_player[i].force_dropping)
9108       murphy_starts_dropping = TRUE;
9109
9110   if (tape.single_step && tape.recording && !tape.pausing)
9111     if (murphy_is_waiting && !murphy_starts_dropping)
9112       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9113
9114   CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9115 }
9116
9117 void CheckSingleStepMode_MM(boolean element_clicked,
9118                             boolean button_released)
9119 {
9120   if (tape.single_step && tape.recording && !tape.pausing)
9121     if (button_released)
9122       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9123
9124   CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9125 }
9126
9127 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9128                          int graphic, int sync_frame, int x, int y)
9129 {
9130   int frame = getGraphicAnimationFrame(graphic, sync_frame);
9131
9132   getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9133 }
9134
9135 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9136 {
9137   return (IS_NEXT_FRAME(sync_frame, graphic));
9138 }
9139
9140 int getGraphicInfo_Delay(int graphic)
9141 {
9142   return graphic_info[graphic].anim_delay;
9143 }
9144
9145 void PlayMenuSoundExt(int sound)
9146 {
9147   if (sound == SND_UNDEFINED)
9148     return;
9149
9150   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9151       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9152     return;
9153
9154   if (IS_LOOP_SOUND(sound))
9155     PlaySoundLoop(sound);
9156   else
9157     PlaySound(sound);
9158 }
9159
9160 void PlayMenuSound()
9161 {
9162   PlayMenuSoundExt(menu.sound[game_status]);
9163 }
9164
9165 void PlayMenuSoundStereo(int sound, int stereo_position)
9166 {
9167   if (sound == SND_UNDEFINED)
9168     return;
9169
9170   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9171       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9172     return;
9173
9174   if (IS_LOOP_SOUND(sound))
9175     PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9176   else
9177     PlaySoundStereo(sound, stereo_position);
9178 }
9179
9180 void PlayMenuSoundIfLoopExt(int sound)
9181 {
9182   if (sound == SND_UNDEFINED)
9183     return;
9184
9185   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9186       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9187     return;
9188
9189   if (IS_LOOP_SOUND(sound))
9190     PlaySoundLoop(sound);
9191 }
9192
9193 void PlayMenuSoundIfLoop()
9194 {
9195   PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9196 }
9197
9198 void PlayMenuMusicExt(int music)
9199 {
9200   if (music == MUS_UNDEFINED)
9201     return;
9202
9203   if (!setup.sound_music)
9204     return;
9205
9206   PlayMusic(music);
9207 }
9208
9209 void PlayMenuMusic()
9210 {
9211   char *curr_music = getCurrentlyPlayingMusicFilename();
9212   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9213
9214   if (!strEqual(curr_music, next_music))
9215     PlayMenuMusicExt(menu.music[game_status]);
9216 }
9217
9218 void PlayMenuSoundsAndMusic()
9219 {
9220   PlayMenuSound();
9221   PlayMenuMusic();
9222 }
9223
9224 static void FadeMenuSounds()
9225 {
9226   FadeSounds();
9227 }
9228
9229 static void FadeMenuMusic()
9230 {
9231   char *curr_music = getCurrentlyPlayingMusicFilename();
9232   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9233
9234   if (!strEqual(curr_music, next_music))
9235     FadeMusic();
9236 }
9237
9238 void FadeMenuSoundsAndMusic()
9239 {
9240   FadeMenuSounds();
9241   FadeMenuMusic();
9242 }
9243
9244 void PlaySoundActivating()
9245 {
9246 #if 0
9247   PlaySound(SND_MENU_ITEM_ACTIVATING);
9248 #endif
9249 }
9250
9251 void PlaySoundSelecting()
9252 {
9253 #if 0
9254   PlaySound(SND_MENU_ITEM_SELECTING);
9255 #endif
9256 }
9257
9258 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
9259 {
9260   boolean change_fullscreen = (setup.fullscreen !=
9261                                video.fullscreen_enabled);
9262   boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9263                                            setup.window_scaling_percent !=
9264                                            video.window_scaling_percent);
9265
9266   if (change_window_scaling_percent && video.fullscreen_enabled)
9267     return;
9268
9269   if (!change_window_scaling_percent && !video.fullscreen_available)
9270     return;
9271
9272 #if defined(TARGET_SDL2)
9273   if (change_window_scaling_percent)
9274   {
9275     SDLSetWindowScaling(setup.window_scaling_percent);
9276
9277     return;
9278   }
9279   else if (change_fullscreen)
9280   {
9281     SDLSetWindowFullscreen(setup.fullscreen);
9282
9283     /* set setup value according to successfully changed fullscreen mode */
9284     setup.fullscreen = video.fullscreen_enabled;
9285
9286     return;
9287   }
9288 #endif
9289
9290   if (change_fullscreen ||
9291       change_window_scaling_percent)
9292   {
9293     Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9294
9295     /* save backbuffer content which gets lost when toggling fullscreen mode */
9296     BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9297
9298     if (change_window_scaling_percent)
9299     {
9300       /* keep window mode, but change window scaling */
9301       video.fullscreen_enabled = TRUE;          /* force new window scaling */
9302     }
9303
9304     /* toggle fullscreen */
9305     ChangeVideoModeIfNeeded(setup.fullscreen);
9306
9307     /* set setup value according to successfully changed fullscreen mode */
9308     setup.fullscreen = video.fullscreen_enabled;
9309
9310     /* restore backbuffer content from temporary backbuffer backup bitmap */
9311     BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9312
9313     FreeBitmap(tmp_backbuffer);
9314
9315     /* update visible window/screen */
9316     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9317   }
9318 }
9319
9320 void JoinRectangles(int *x, int *y, int *width, int *height,
9321                     int x2, int y2, int width2, int height2)
9322 {
9323   // do not join with "off-screen" rectangle
9324   if (x2 == -1 || y2 == -1)
9325     return;
9326
9327   *x = MIN(*x, x2);
9328   *y = MIN(*y, y2);
9329   *width = MAX(*width, width2);
9330   *height = MAX(*height, height2);
9331 }
9332
9333 void SetAnimStatus(int anim_status_new)
9334 {
9335   if (anim_status_new == GAME_MODE_MAIN)
9336     anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9337   else if (anim_status_new == GAME_MODE_SCORES)
9338     anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9339
9340   global.anim_status_next = anim_status_new;
9341
9342   // directly set screen modes that are entered without fading
9343   if ((global.anim_status      == GAME_MODE_PSEUDO_MAINONLY &&
9344        global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9345       (global.anim_status      == GAME_MODE_PSEUDO_TYPENAME &&
9346        global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9347     global.anim_status = global.anim_status_next;
9348 }
9349
9350 void SetGameStatus(int game_status_new)
9351 {
9352   if (game_status_new != game_status)
9353     game_status_last_screen = game_status;
9354
9355   game_status = game_status_new;
9356
9357   SetAnimStatus(game_status_new);
9358 }
9359
9360 void SetFontStatus(int game_status_new)
9361 {
9362   static int last_game_status = -1;
9363
9364   if (game_status_new != -1)
9365   {
9366     // set game status for font use after storing last game status
9367     last_game_status = game_status;
9368     game_status = game_status_new;
9369   }
9370   else
9371   {
9372     // reset game status after font use from last stored game status
9373     game_status = last_game_status;
9374   }
9375 }
9376
9377 void ResetFontStatus()
9378 {
9379   SetFontStatus(-1);
9380 }
9381
9382 boolean CheckIfPlayfieldViewportHasChanged()
9383 {
9384   // if game status has not changed, playfield viewport has not changed either
9385   if (game_status == game_status_last)
9386     return FALSE;
9387
9388   // check if playfield viewport has changed with current game status
9389   struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9390   int new_real_sx       = vp_playfield->x;
9391   int new_real_sy       = vp_playfield->y;
9392   int new_full_sxsize   = vp_playfield->width;
9393   int new_full_sysize   = vp_playfield->height;
9394
9395   return (new_real_sx != REAL_SX ||
9396           new_real_sy != REAL_SY ||
9397           new_full_sxsize != FULL_SXSIZE ||
9398           new_full_sysize != FULL_SYSIZE);
9399 }
9400
9401 boolean CheckIfGlobalBorderOrPlayfieldViewportHasChanged()
9402 {
9403   return (CheckIfGlobalBorderHasChanged() ||
9404           CheckIfPlayfieldViewportHasChanged());
9405 }
9406
9407 void ChangeViewportPropertiesIfNeeded()
9408 {
9409   boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9410                                FALSE : setup.small_game_graphics);
9411   int gfx_game_mode = game_status;
9412   int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9413                         game_status);
9414   struct RectWithBorder *vp_window    = &viewport.window[gfx_game_mode];
9415   struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9416   struct RectWithBorder *vp_door_1    = &viewport.door_1[gfx_game_mode];
9417   struct RectWithBorder *vp_door_2    = &viewport.door_2[gfx_game_mode2];
9418   struct RectWithBorder *vp_door_3    = &viewport.door_2[GAME_MODE_EDITOR];
9419   int new_win_xsize     = vp_window->width;
9420   int new_win_ysize     = vp_window->height;
9421   int border_size       = vp_playfield->border_size;
9422   int new_sx            = vp_playfield->x + border_size;
9423   int new_sy            = vp_playfield->y + border_size;
9424   int new_sxsize        = vp_playfield->width  - 2 * border_size;
9425   int new_sysize        = vp_playfield->height - 2 * border_size;
9426   int new_real_sx       = vp_playfield->x;
9427   int new_real_sy       = vp_playfield->y;
9428   int new_full_sxsize   = vp_playfield->width;
9429   int new_full_sysize   = vp_playfield->height;
9430   int new_dx            = vp_door_1->x;
9431   int new_dy            = vp_door_1->y;
9432   int new_dxsize        = vp_door_1->width;
9433   int new_dysize        = vp_door_1->height;
9434   int new_vx            = vp_door_2->x;
9435   int new_vy            = vp_door_2->y;
9436   int new_vxsize        = vp_door_2->width;
9437   int new_vysize        = vp_door_2->height;
9438   int new_ex            = vp_door_3->x;
9439   int new_ey            = vp_door_3->y;
9440   int new_exsize        = vp_door_3->width;
9441   int new_eysize        = vp_door_3->height;
9442   int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9443   int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9444                   gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9445   int new_scr_fieldx = new_sxsize / tilesize;
9446   int new_scr_fieldy = new_sysize / tilesize;
9447   int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9448   int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9449   boolean init_gfx_buffers = FALSE;
9450   boolean init_video_buffer = FALSE;
9451   boolean init_gadgets_and_anims = FALSE;
9452   boolean init_em_graphics = FALSE;
9453
9454   if (new_win_xsize != WIN_XSIZE ||
9455       new_win_ysize != WIN_YSIZE)
9456   {
9457     WIN_XSIZE = new_win_xsize;
9458     WIN_YSIZE = new_win_ysize;
9459
9460     init_video_buffer = TRUE;
9461     init_gfx_buffers = TRUE;
9462     init_gadgets_and_anims = TRUE;
9463
9464     // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9465   }
9466
9467   if (new_scr_fieldx != SCR_FIELDX ||
9468       new_scr_fieldy != SCR_FIELDY)
9469   {
9470     /* this always toggles between MAIN and GAME when using small tile size */
9471
9472     SCR_FIELDX = new_scr_fieldx;
9473     SCR_FIELDY = new_scr_fieldy;
9474
9475     // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9476   }
9477
9478   if (new_sx != SX ||
9479       new_sy != SY ||
9480       new_dx != DX ||
9481       new_dy != DY ||
9482       new_vx != VX ||
9483       new_vy != VY ||
9484       new_ex != EX ||
9485       new_ey != EY ||
9486       new_sxsize != SXSIZE ||
9487       new_sysize != SYSIZE ||
9488       new_dxsize != DXSIZE ||
9489       new_dysize != DYSIZE ||
9490       new_vxsize != VXSIZE ||
9491       new_vysize != VYSIZE ||
9492       new_exsize != EXSIZE ||
9493       new_eysize != EYSIZE ||
9494       new_real_sx != REAL_SX ||
9495       new_real_sy != REAL_SY ||
9496       new_full_sxsize != FULL_SXSIZE ||
9497       new_full_sysize != FULL_SYSIZE ||
9498       new_tilesize_var != TILESIZE_VAR
9499       )
9500   {
9501     // ------------------------------------------------------------------------
9502     // determine next fading area for changed viewport definitions
9503     // ------------------------------------------------------------------------
9504
9505     // start with current playfield area (default fading area)
9506     FADE_SX = REAL_SX;
9507     FADE_SY = REAL_SY;
9508     FADE_SXSIZE = FULL_SXSIZE;
9509     FADE_SYSIZE = FULL_SYSIZE;
9510
9511     // add new playfield area if position or size has changed
9512     if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9513         new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9514     {
9515       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9516                      new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9517     }
9518
9519     // add current and new door 1 area if position or size has changed
9520     if (new_dx != DX || new_dy != DY ||
9521         new_dxsize != DXSIZE || new_dysize != DYSIZE)
9522     {
9523       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9524                      DX, DY, DXSIZE, DYSIZE);
9525       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9526                      new_dx, new_dy, new_dxsize, new_dysize);
9527     }
9528
9529     // add current and new door 2 area if position or size has changed
9530     if (new_dx != VX || new_dy != VY ||
9531         new_dxsize != VXSIZE || new_dysize != VYSIZE)
9532     {
9533       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9534                      VX, VY, VXSIZE, VYSIZE);
9535       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9536                      new_vx, new_vy, new_vxsize, new_vysize);
9537     }
9538
9539     // ------------------------------------------------------------------------
9540     // handle changed tile size
9541     // ------------------------------------------------------------------------
9542
9543     if (new_tilesize_var != TILESIZE_VAR)
9544     {
9545       // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9546
9547       // changing tile size invalidates scroll values of engine snapshots
9548       FreeEngineSnapshotSingle();
9549
9550       // changing tile size requires update of graphic mapping for EM engine
9551       init_em_graphics = TRUE;
9552     }
9553
9554     SX = new_sx;
9555     SY = new_sy;
9556     DX = new_dx;
9557     DY = new_dy;
9558     VX = new_vx;
9559     VY = new_vy;
9560     EX = new_ex;
9561     EY = new_ey;
9562     SXSIZE = new_sxsize;
9563     SYSIZE = new_sysize;
9564     DXSIZE = new_dxsize;
9565     DYSIZE = new_dysize;
9566     VXSIZE = new_vxsize;
9567     VYSIZE = new_vysize;
9568     EXSIZE = new_exsize;
9569     EYSIZE = new_eysize;
9570     REAL_SX = new_real_sx;
9571     REAL_SY = new_real_sy;
9572     FULL_SXSIZE = new_full_sxsize;
9573     FULL_SYSIZE = new_full_sysize;
9574     TILESIZE_VAR = new_tilesize_var;
9575
9576     init_gfx_buffers = TRUE;
9577     init_gadgets_and_anims = TRUE;
9578
9579     // printf("::: viewports: init_gfx_buffers\n");
9580     // printf("::: viewports: init_gadgets_and_anims\n");
9581   }
9582
9583   if (init_gfx_buffers)
9584   {
9585     // printf("::: init_gfx_buffers\n");
9586
9587     SCR_FIELDX = new_scr_fieldx_buffers;
9588     SCR_FIELDY = new_scr_fieldy_buffers;
9589
9590     InitGfxBuffers();
9591
9592     SCR_FIELDX = new_scr_fieldx;
9593     SCR_FIELDY = new_scr_fieldy;
9594
9595     SetDrawDeactivationMask(REDRAW_NONE);
9596     SetDrawBackgroundMask(REDRAW_FIELD);
9597   }
9598
9599   if (init_video_buffer)
9600   {
9601     // printf("::: init_video_buffer\n");
9602
9603     InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9604     InitImageTextures();
9605   }
9606
9607   if (init_gadgets_and_anims)
9608   {
9609     // printf("::: init_gadgets_and_anims\n");
9610
9611     InitGadgets();
9612     InitGlobalAnimations();
9613   }
9614
9615   if (init_em_graphics)
9616   {
9617       InitGraphicInfo_EM();
9618   }
9619 }