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