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