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