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