removed options to prevent initial rolling objects in native EM levels
[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     Xacid_splash_e,                     FALSE,  FALSE,
5749     EL_ACID_SPLASH_RIGHT,               -1, -1
5750   },
5751   {
5752     Xacid_splash_w,                     FALSE,  FALSE,
5753     EL_ACID_SPLASH_LEFT,                -1, -1
5754   },
5755   {
5756     Xpush_stone_e,                      FALSE,  FALSE,
5757     EL_ROCK,                            -1, MV_BIT_RIGHT
5758   },
5759   {
5760     Xpush_stone_w,                      FALSE,  FALSE,
5761     EL_ROCK,                            -1, MV_BIT_LEFT
5762   },
5763   {
5764     Xpush_nut_e,                        FALSE,  FALSE,
5765     EL_NUT,                             -1, MV_BIT_RIGHT
5766   },
5767   {
5768     Xpush_nut_w,                        FALSE,  FALSE,
5769     EL_NUT,                             -1, MV_BIT_LEFT
5770   },
5771   {
5772     Xpush_spring_e,                     FALSE,  FALSE,
5773     EL_SPRING,                          -1, MV_BIT_RIGHT
5774   },
5775   {
5776     Xpush_spring_w,                     FALSE,  FALSE,
5777     EL_SPRING,                          -1, MV_BIT_LEFT
5778   },
5779   {
5780     Xpush_emerald_e,                    FALSE,  FALSE,
5781     EL_EMERALD,                         -1, MV_BIT_RIGHT
5782   },
5783   {
5784     Xpush_emerald_w,                    FALSE,  FALSE,
5785     EL_EMERALD,                         -1, MV_BIT_LEFT
5786   },
5787   {
5788     Xpush_diamond_e,                    FALSE,  FALSE,
5789     EL_DIAMOND,                         -1, MV_BIT_RIGHT
5790   },
5791   {
5792     Xpush_diamond_w,                    FALSE,  FALSE,
5793     EL_DIAMOND,                         -1, MV_BIT_LEFT
5794   },
5795   {
5796     Xpush_bomb_e,                       FALSE,  FALSE,
5797     EL_BOMB,                            -1, MV_BIT_RIGHT
5798   },
5799   {
5800     Xpush_bomb_w,                       FALSE,  FALSE,
5801     EL_BOMB,                            -1, MV_BIT_LEFT
5802   },
5803   {
5804     Xstone,                             TRUE,   FALSE,
5805     EL_ROCK,                            -1, -1
5806   },
5807   {
5808     Xstone_pause,                       FALSE,  FALSE,
5809     EL_ROCK,                            -1, -1
5810   },
5811   {
5812     Xstone_fall,                        FALSE,  FALSE,
5813     EL_ROCK,                            -1, -1
5814   },
5815   {
5816     Ystone_s,                           FALSE,  FALSE,
5817     EL_ROCK,                            ACTION_FALLING, -1
5818   },
5819   {
5820     Ystone_sB,                          FALSE,  TRUE,
5821     EL_ROCK,                            ACTION_FALLING, -1
5822   },
5823   {
5824     Ystone_e,                           FALSE,  FALSE,
5825     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
5826   },
5827   {
5828     Ystone_eB,                          FALSE,  TRUE,
5829     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
5830   },
5831   {
5832     Ystone_w,                           FALSE,  FALSE,
5833     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
5834   },
5835   {
5836     Ystone_wB,                          FALSE,  TRUE,
5837     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
5838   },
5839   {
5840     Xnut,                               TRUE,   FALSE,
5841     EL_NUT,                             -1, -1
5842   },
5843   {
5844     Xnut_pause,                         FALSE,  FALSE,
5845     EL_NUT,                             -1, -1
5846   },
5847   {
5848     Xnut_fall,                          FALSE,  FALSE,
5849     EL_NUT,                             -1, -1
5850   },
5851   {
5852     Ynut_s,                             FALSE,  FALSE,
5853     EL_NUT,                             ACTION_FALLING, -1
5854   },
5855   {
5856     Ynut_sB,                            FALSE,  TRUE,
5857     EL_NUT,                             ACTION_FALLING, -1
5858   },
5859   {
5860     Ynut_e,                             FALSE,  FALSE,
5861     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
5862   },
5863   {
5864     Ynut_eB,                            FALSE,  TRUE,
5865     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
5866   },
5867   {
5868     Ynut_w,                             FALSE,  FALSE,
5869     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
5870   },
5871   {
5872     Ynut_wB,                            FALSE,  TRUE,
5873     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
5874   },
5875   {
5876     Xbug_1_n,                           TRUE,   FALSE,
5877     EL_BUG_UP,                          -1, -1
5878   },
5879   {
5880     Xbug_1_e,                           TRUE,   FALSE,
5881     EL_BUG_RIGHT,                       -1, -1
5882   },
5883   {
5884     Xbug_1_s,                           TRUE,   FALSE,
5885     EL_BUG_DOWN,                        -1, -1
5886   },
5887   {
5888     Xbug_1_w,                           TRUE,   FALSE,
5889     EL_BUG_LEFT,                        -1, -1
5890   },
5891   {
5892     Xbug_2_n,                           FALSE,  FALSE,
5893     EL_BUG_UP,                          -1, -1
5894   },
5895   {
5896     Xbug_2_e,                           FALSE,  FALSE,
5897     EL_BUG_RIGHT,                       -1, -1
5898   },
5899   {
5900     Xbug_2_s,                           FALSE,  FALSE,
5901     EL_BUG_DOWN,                        -1, -1
5902   },
5903   {
5904     Xbug_2_w,                           FALSE,  FALSE,
5905     EL_BUG_LEFT,                        -1, -1
5906   },
5907   {
5908     Ybug_n,                             FALSE,  FALSE,
5909     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
5910   },
5911   {
5912     Ybug_nB,                            FALSE,  TRUE,
5913     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
5914   },
5915   {
5916     Ybug_e,                             FALSE,  FALSE,
5917     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
5918   },
5919   {
5920     Ybug_eB,                            FALSE,  TRUE,
5921     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
5922   },
5923   {
5924     Ybug_s,                             FALSE,  FALSE,
5925     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
5926   },
5927   {
5928     Ybug_sB,                            FALSE,  TRUE,
5929     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
5930   },
5931   {
5932     Ybug_w,                             FALSE,  FALSE,
5933     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
5934   },
5935   {
5936     Ybug_wB,                            FALSE,  TRUE,
5937     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
5938   },
5939   {
5940     Ybug_w_n,                           FALSE,  FALSE,
5941     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5942   },
5943   {
5944     Ybug_n_e,                           FALSE,  FALSE,
5945     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5946   },
5947   {
5948     Ybug_e_s,                           FALSE,  FALSE,
5949     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5950   },
5951   {
5952     Ybug_s_w,                           FALSE,  FALSE,
5953     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5954   },
5955   {
5956     Ybug_e_n,                           FALSE,  FALSE,
5957     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5958   },
5959   {
5960     Ybug_s_e,                           FALSE,  FALSE,
5961     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5962   },
5963   {
5964     Ybug_w_s,                           FALSE,  FALSE,
5965     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5966   },
5967   {
5968     Ybug_n_w,                           FALSE,  FALSE,
5969     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5970   },
5971   {
5972     Ybug_stone,                         FALSE,  FALSE,
5973     EL_BUG,                             ACTION_SMASHED_BY_ROCK, -1
5974   },
5975   {
5976     Ybug_spring,                        FALSE,  FALSE,
5977     EL_BUG,                             ACTION_SMASHED_BY_SPRING, -1
5978   },
5979   {
5980     Xtank_1_n,                          TRUE,   FALSE,
5981     EL_SPACESHIP_UP,                    -1, -1
5982   },
5983   {
5984     Xtank_1_e,                          TRUE,   FALSE,
5985     EL_SPACESHIP_RIGHT,                 -1, -1
5986   },
5987   {
5988     Xtank_1_s,                          TRUE,   FALSE,
5989     EL_SPACESHIP_DOWN,                  -1, -1
5990   },
5991   {
5992     Xtank_1_w,                          TRUE,   FALSE,
5993     EL_SPACESHIP_LEFT,                  -1, -1
5994   },
5995   {
5996     Xtank_2_n,                          FALSE,  FALSE,
5997     EL_SPACESHIP_UP,                    -1, -1
5998   },
5999   {
6000     Xtank_2_e,                          FALSE,  FALSE,
6001     EL_SPACESHIP_RIGHT,                 -1, -1
6002   },
6003   {
6004     Xtank_2_s,                          FALSE,  FALSE,
6005     EL_SPACESHIP_DOWN,                  -1, -1
6006   },
6007   {
6008     Xtank_2_w,                          FALSE,  FALSE,
6009     EL_SPACESHIP_LEFT,                  -1, -1
6010   },
6011   {
6012     Ytank_n,                            FALSE,  FALSE,
6013     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
6014   },
6015   {
6016     Ytank_nB,                           FALSE,  TRUE,
6017     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
6018   },
6019   {
6020     Ytank_e,                            FALSE,  FALSE,
6021     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
6022   },
6023   {
6024     Ytank_eB,                           FALSE,  TRUE,
6025     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
6026   },
6027   {
6028     Ytank_s,                            FALSE,  FALSE,
6029     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
6030   },
6031   {
6032     Ytank_sB,                           FALSE,  TRUE,
6033     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
6034   },
6035   {
6036     Ytank_w,                            FALSE,  FALSE,
6037     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
6038   },
6039   {
6040     Ytank_wB,                           FALSE,  TRUE,
6041     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
6042   },
6043   {
6044     Ytank_w_n,                          FALSE,  FALSE,
6045     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6046   },
6047   {
6048     Ytank_n_e,                          FALSE,  FALSE,
6049     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6050   },
6051   {
6052     Ytank_e_s,                          FALSE,  FALSE,
6053     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6054   },
6055   {
6056     Ytank_s_w,                          FALSE,  FALSE,
6057     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6058   },
6059   {
6060     Ytank_e_n,                          FALSE,  FALSE,
6061     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6062   },
6063   {
6064     Ytank_s_e,                          FALSE,  FALSE,
6065     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6066   },
6067   {
6068     Ytank_w_s,                          FALSE,  FALSE,
6069     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6070   },
6071   {
6072     Ytank_n_w,                          FALSE,  FALSE,
6073     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6074   },
6075   {
6076     Ytank_stone,                        FALSE,  FALSE,
6077     EL_SPACESHIP,                       ACTION_SMASHED_BY_ROCK, -1
6078   },
6079   {
6080     Ytank_spring,                       FALSE,  FALSE,
6081     EL_SPACESHIP,                       ACTION_SMASHED_BY_SPRING, -1
6082   },
6083   {
6084     Xandroid,                           TRUE,   FALSE,
6085     EL_EMC_ANDROID,                     ACTION_ACTIVE, -1
6086   },
6087   {
6088     Xandroid_1_n,                       FALSE,  FALSE,
6089     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
6090   },
6091   {
6092     Xandroid_2_n,                       FALSE,  FALSE,
6093     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
6094   },
6095   {
6096     Xandroid_1_e,                       FALSE,  FALSE,
6097     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
6098   },
6099   {
6100     Xandroid_2_e,                       FALSE,  FALSE,
6101     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
6102   },
6103   {
6104     Xandroid_1_w,                       FALSE,  FALSE,
6105     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
6106   },
6107   {
6108     Xandroid_2_w,                       FALSE,  FALSE,
6109     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
6110   },
6111   {
6112     Xandroid_1_s,                       FALSE,  FALSE,
6113     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
6114   },
6115   {
6116     Xandroid_2_s,                       FALSE,  FALSE,
6117     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
6118   },
6119   {
6120     Yandroid_n,                         FALSE,  FALSE,
6121     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
6122   },
6123   {
6124     Yandroid_nB,                        FALSE,  TRUE,
6125     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
6126   },
6127   {
6128     Yandroid_ne,                        FALSE,  FALSE,
6129     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPRIGHT
6130   },
6131   {
6132     Yandroid_neB,                       FALSE,  TRUE,
6133     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPRIGHT
6134   },
6135   {
6136     Yandroid_e,                         FALSE,  FALSE,
6137     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
6138   },
6139   {
6140     Yandroid_eB,                        FALSE,  TRUE,
6141     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
6142   },
6143   {
6144     Yandroid_se,                        FALSE,  FALSE,
6145     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNRIGHT
6146   },
6147   {
6148     Yandroid_seB,                       FALSE,  TRUE,
6149     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6150   },
6151   {
6152     Yandroid_s,                         FALSE,  FALSE,
6153     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
6154   },
6155   {
6156     Yandroid_sB,                        FALSE,  TRUE,
6157     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
6158   },
6159   {
6160     Yandroid_sw,                        FALSE,  FALSE,
6161     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNLEFT
6162   },
6163   {
6164     Yandroid_swB,                       FALSE,  TRUE,
6165     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNLEFT
6166   },
6167   {
6168     Yandroid_w,                         FALSE,  FALSE,
6169     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
6170   },
6171   {
6172     Yandroid_wB,                        FALSE,  TRUE,
6173     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
6174   },
6175   {
6176     Yandroid_nw,                        FALSE,  FALSE,
6177     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPLEFT
6178   },
6179   {
6180     Yandroid_nwB,                       FALSE,  TRUE,
6181     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPLEFT
6182   },
6183   {
6184     Xspring,                            TRUE,   FALSE,
6185     EL_SPRING,                          -1, -1
6186   },
6187   {
6188     Xspring_pause,                      FALSE,  FALSE,
6189     EL_SPRING,                          -1, -1
6190   },
6191   {
6192     Xspring_e,                          FALSE,  FALSE,
6193     EL_SPRING,                          -1, -1
6194   },
6195   {
6196     Xspring_w,                          FALSE,  FALSE,
6197     EL_SPRING,                          -1, -1
6198   },
6199   {
6200     Xspring_fall,                       FALSE,  FALSE,
6201     EL_SPRING,                          -1, -1
6202   },
6203   {
6204     Yspring_s,                          FALSE,  FALSE,
6205     EL_SPRING,                          ACTION_FALLING, -1
6206   },
6207   {
6208     Yspring_sB,                         FALSE,  TRUE,
6209     EL_SPRING,                          ACTION_FALLING, -1
6210   },
6211   {
6212     Yspring_e,                          FALSE,  FALSE,
6213     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
6214   },
6215   {
6216     Yspring_eB,                         FALSE,  TRUE,
6217     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
6218   },
6219   {
6220     Yspring_w,                          FALSE,  FALSE,
6221     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
6222   },
6223   {
6224     Yspring_wB,                         FALSE,  TRUE,
6225     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
6226   },
6227   {
6228     Yspring_alien_e,                    FALSE,  FALSE,
6229     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
6230   },
6231   {
6232     Yspring_alien_eB,                   FALSE,  TRUE,
6233     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
6234   },
6235   {
6236     Yspring_alien_w,                    FALSE,  FALSE,
6237     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
6238   },
6239   {
6240     Yspring_alien_wB,                   FALSE,  TRUE,
6241     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
6242   },
6243   {
6244     Xeater_n,                           TRUE,   FALSE,
6245     EL_YAMYAM_UP,                       -1, -1
6246   },
6247   {
6248     Xeater_e,                           TRUE,   FALSE,
6249     EL_YAMYAM_RIGHT,                    -1, -1
6250   },
6251   {
6252     Xeater_w,                           TRUE,   FALSE,
6253     EL_YAMYAM_LEFT,                     -1, -1
6254   },
6255   {
6256     Xeater_s,                           TRUE,   FALSE,
6257     EL_YAMYAM_DOWN,                     -1, -1
6258   },
6259   {
6260     Yeater_n,                           FALSE,  FALSE,
6261     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
6262   },
6263   {
6264     Yeater_nB,                          FALSE,  TRUE,
6265     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
6266   },
6267   {
6268     Yeater_e,                           FALSE,  FALSE,
6269     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
6270   },
6271   {
6272     Yeater_eB,                          FALSE,  TRUE,
6273     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
6274   },
6275   {
6276     Yeater_s,                           FALSE,  FALSE,
6277     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
6278   },
6279   {
6280     Yeater_sB,                          FALSE,  TRUE,
6281     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
6282   },
6283   {
6284     Yeater_w,                           FALSE,  FALSE,
6285     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
6286   },
6287   {
6288     Yeater_wB,                          FALSE,  TRUE,
6289     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
6290   },
6291   {
6292     Yeater_stone,                       FALSE,  FALSE,
6293     EL_YAMYAM,                          ACTION_SMASHED_BY_ROCK, -1
6294   },
6295   {
6296     Yeater_spring,                      FALSE,  FALSE,
6297     EL_YAMYAM,                          ACTION_SMASHED_BY_SPRING, -1
6298   },
6299   {
6300     Xalien,                             TRUE,   FALSE,
6301     EL_ROBOT,                           -1, -1
6302   },
6303   {
6304     Xalien_pause,                       FALSE,  FALSE,
6305     EL_ROBOT,                           -1, -1
6306   },
6307   {
6308     Yalien_n,                           FALSE,  FALSE,
6309     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
6310   },
6311   {
6312     Yalien_nB,                          FALSE,  TRUE,
6313     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
6314   },
6315   {
6316     Yalien_e,                           FALSE,  FALSE,
6317     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
6318   },
6319   {
6320     Yalien_eB,                          FALSE,  TRUE,
6321     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
6322   },
6323   {
6324     Yalien_s,                           FALSE,  FALSE,
6325     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
6326   },
6327   {
6328     Yalien_sB,                          FALSE,  TRUE,
6329     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
6330   },
6331   {
6332     Yalien_w,                           FALSE,  FALSE,
6333     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
6334   },
6335   {
6336     Yalien_wB,                          FALSE,  TRUE,
6337     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
6338   },
6339   {
6340     Yalien_stone,                       FALSE,  FALSE,
6341     EL_ROBOT,                           ACTION_SMASHED_BY_ROCK, -1
6342   },
6343   {
6344     Yalien_spring,                      FALSE,  FALSE,
6345     EL_ROBOT,                           ACTION_SMASHED_BY_SPRING, -1
6346   },
6347   {
6348     Xemerald,                           TRUE,   FALSE,
6349     EL_EMERALD,                         -1, -1
6350   },
6351   {
6352     Xemerald_pause,                     FALSE,  FALSE,
6353     EL_EMERALD,                         -1, -1
6354   },
6355   {
6356     Xemerald_fall,                      FALSE,  FALSE,
6357     EL_EMERALD,                         -1, -1
6358   },
6359   {
6360     Xemerald_shine,                     FALSE,  FALSE,
6361     EL_EMERALD,                         ACTION_TWINKLING, -1
6362   },
6363   {
6364     Yemerald_s,                         FALSE,  FALSE,
6365     EL_EMERALD,                         ACTION_FALLING, -1
6366   },
6367   {
6368     Yemerald_sB,                        FALSE,  TRUE,
6369     EL_EMERALD,                         ACTION_FALLING, -1
6370   },
6371   {
6372     Yemerald_e,                         FALSE,  FALSE,
6373     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
6374   },
6375   {
6376     Yemerald_eB,                        FALSE,  TRUE,
6377     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
6378   },
6379   {
6380     Yemerald_w,                         FALSE,  FALSE,
6381     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
6382   },
6383   {
6384     Yemerald_wB,                        FALSE,  TRUE,
6385     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
6386   },
6387   {
6388     Yemerald_blank,                     FALSE,  FALSE,
6389     EL_EMERALD,                         ACTION_COLLECTING, -1
6390   },
6391   {
6392     Ynut_stone,                         FALSE,  FALSE,
6393     EL_NUT,                             ACTION_BREAKING, -1
6394   },
6395   {
6396     Xdiamond,                           TRUE,   FALSE,
6397     EL_DIAMOND,                         -1, -1
6398   },
6399   {
6400     Xdiamond_pause,                     FALSE,  FALSE,
6401     EL_DIAMOND,                         -1, -1
6402   },
6403   {
6404     Xdiamond_fall,                      FALSE,  FALSE,
6405     EL_DIAMOND,                         -1, -1
6406   },
6407   {
6408     Xdiamond_shine,                     FALSE,  FALSE,
6409     EL_DIAMOND,                         ACTION_TWINKLING, -1
6410   },
6411   {
6412     Ydiamond_s,                         FALSE,  FALSE,
6413     EL_DIAMOND,                         ACTION_FALLING, -1
6414   },
6415   {
6416     Ydiamond_sB,                        FALSE,  TRUE,
6417     EL_DIAMOND,                         ACTION_FALLING, -1
6418   },
6419   {
6420     Ydiamond_e,                         FALSE,  FALSE,
6421     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
6422   },
6423   {
6424     Ydiamond_eB,                        FALSE,  TRUE,
6425     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
6426   },
6427   {
6428     Ydiamond_w,                         FALSE,  FALSE,
6429     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
6430   },
6431   {
6432     Ydiamond_wB,                        FALSE,  TRUE,
6433     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
6434   },
6435   {
6436     Ydiamond_blank,                     FALSE,  FALSE,
6437     EL_DIAMOND,                         ACTION_COLLECTING, -1
6438   },
6439   {
6440     Ydiamond_stone,                     FALSE,  FALSE,
6441     EL_DIAMOND,                         ACTION_SMASHED_BY_ROCK, -1
6442   },
6443   {
6444     Xdrip_fall,                         TRUE,   FALSE,
6445     EL_AMOEBA_DROP,                     -1, -1
6446   },
6447   {
6448     Xdrip_stretch,                      FALSE,  FALSE,
6449     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6450   },
6451   {
6452     Xdrip_stretchB,                     FALSE,  TRUE,
6453     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6454   },
6455   {
6456     Xdrip,                              FALSE,  FALSE,
6457     EL_AMOEBA_DROP,                     ACTION_GROWING, -1
6458   },
6459   {
6460     Ydrip_1_s,                          FALSE,  FALSE,
6461     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6462   },
6463   {
6464     Ydrip_1_sB,                         FALSE,  TRUE,
6465     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6466   },
6467   {
6468     Ydrip_2_s,                          FALSE,  FALSE,
6469     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6470   },
6471   {
6472     Ydrip_2_sB,                         FALSE,  TRUE,
6473     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
6474   },
6475   {
6476     Xbomb,                              TRUE,   FALSE,
6477     EL_BOMB,                            -1, -1
6478   },
6479   {
6480     Xbomb_pause,                        FALSE,  FALSE,
6481     EL_BOMB,                            -1, -1
6482   },
6483   {
6484     Xbomb_fall,                         FALSE,  FALSE,
6485     EL_BOMB,                            -1, -1
6486   },
6487   {
6488     Ybomb_s,                            FALSE,  FALSE,
6489     EL_BOMB,                            ACTION_FALLING, -1
6490   },
6491   {
6492     Ybomb_sB,                           FALSE,  TRUE,
6493     EL_BOMB,                            ACTION_FALLING, -1
6494   },
6495   {
6496     Ybomb_e,                            FALSE,  FALSE,
6497     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
6498   },
6499   {
6500     Ybomb_eB,                           FALSE,  TRUE,
6501     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
6502   },
6503   {
6504     Ybomb_w,                            FALSE,  FALSE,
6505     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
6506   },
6507   {
6508     Ybomb_wB,                           FALSE,  TRUE,
6509     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
6510   },
6511   {
6512     Ybomb_blank,                        FALSE,  FALSE,
6513     EL_BOMB,                            ACTION_ACTIVATING, -1
6514   },
6515   {
6516     Xballoon,                           TRUE,   FALSE,
6517     EL_BALLOON,                         -1, -1
6518   },
6519   {
6520     Yballoon_n,                         FALSE,  FALSE,
6521     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
6522   },
6523   {
6524     Yballoon_nB,                        FALSE,  TRUE,
6525     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
6526   },
6527   {
6528     Yballoon_e,                         FALSE,  FALSE,
6529     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
6530   },
6531   {
6532     Yballoon_eB,                        FALSE,  TRUE,
6533     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
6534   },
6535   {
6536     Yballoon_s,                         FALSE,  FALSE,
6537     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
6538   },
6539   {
6540     Yballoon_sB,                        FALSE,  TRUE,
6541     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
6542   },
6543   {
6544     Yballoon_w,                         FALSE,  FALSE,
6545     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
6546   },
6547   {
6548     Yballoon_wB,                        FALSE,  TRUE,
6549     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
6550   },
6551   {
6552     Xgrass,                             TRUE,   FALSE,
6553     EL_EMC_GRASS,                       -1, -1
6554   },
6555   {
6556     Ygrass_nB,                          FALSE,  FALSE,
6557     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_UP
6558   },
6559   {
6560     Ygrass_eB,                          FALSE,  FALSE,
6561     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_RIGHT
6562   },
6563   {
6564     Ygrass_sB,                          FALSE,  FALSE,
6565     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_DOWN
6566   },
6567   {
6568     Ygrass_wB,                          FALSE,  FALSE,
6569     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_LEFT
6570   },
6571   {
6572     Xdirt,                              TRUE,   FALSE,
6573     EL_SAND,                            -1, -1
6574   },
6575   {
6576     Ydirt_nB,                           FALSE,  FALSE,
6577     EL_SAND,                            ACTION_DIGGING, MV_BIT_UP
6578   },
6579   {
6580     Ydirt_eB,                           FALSE,  FALSE,
6581     EL_SAND,                            ACTION_DIGGING, MV_BIT_RIGHT
6582   },
6583   {
6584     Ydirt_sB,                           FALSE,  FALSE,
6585     EL_SAND,                            ACTION_DIGGING, MV_BIT_DOWN
6586   },
6587   {
6588     Ydirt_wB,                           FALSE,  FALSE,
6589     EL_SAND,                            ACTION_DIGGING, MV_BIT_LEFT
6590   },
6591   {
6592     Xacid_ne,                           TRUE,   FALSE,
6593     EL_ACID_POOL_TOPRIGHT,              -1, -1
6594   },
6595   {
6596     Xacid_se,                           TRUE,   FALSE,
6597     EL_ACID_POOL_BOTTOMRIGHT,           -1, -1
6598   },
6599   {
6600     Xacid_s,                            TRUE,   FALSE,
6601     EL_ACID_POOL_BOTTOM,                -1, -1
6602   },
6603   {
6604     Xacid_sw,                           TRUE,   FALSE,
6605     EL_ACID_POOL_BOTTOMLEFT,            -1, -1
6606   },
6607   {
6608     Xacid_nw,                           TRUE,   FALSE,
6609     EL_ACID_POOL_TOPLEFT,               -1, -1
6610   },
6611   {
6612     Xacid_1,                            TRUE,   FALSE,
6613     EL_ACID,                            -1, -1
6614   },
6615   {
6616     Xacid_2,                            FALSE,  FALSE,
6617     EL_ACID,                            -1, -1
6618   },
6619   {
6620     Xacid_3,                            FALSE,  FALSE,
6621     EL_ACID,                            -1, -1
6622   },
6623   {
6624     Xacid_4,                            FALSE,  FALSE,
6625     EL_ACID,                            -1, -1
6626   },
6627   {
6628     Xacid_5,                            FALSE,  FALSE,
6629     EL_ACID,                            -1, -1
6630   },
6631   {
6632     Xacid_6,                            FALSE,  FALSE,
6633     EL_ACID,                            -1, -1
6634   },
6635   {
6636     Xacid_7,                            FALSE,  FALSE,
6637     EL_ACID,                            -1, -1
6638   },
6639   {
6640     Xacid_8,                            FALSE,  FALSE,
6641     EL_ACID,                            -1, -1
6642   },
6643   {
6644     Xball_1,                            TRUE,   FALSE,
6645     EL_EMC_MAGIC_BALL,                  -1, -1
6646   },
6647   {
6648     Yball_1,                            FALSE,  FALSE,
6649     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
6650   },
6651   {
6652     Xball_2,                            FALSE,  FALSE,
6653     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
6654   },
6655   {
6656     Yball_2,                            FALSE,  FALSE,
6657     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
6658   },
6659   {
6660     Yball_blank,                        FALSE,  FALSE,
6661     EL_EMC_MAGIC_BALL,                  ACTION_DROPPING, -1
6662   },
6663   {
6664     Ykey_1_blank,                       FALSE,  FALSE,
6665     EL_EM_KEY_1,                        ACTION_COLLECTING, -1
6666   },
6667   {
6668     Ykey_2_blank,                       FALSE,  FALSE,
6669     EL_EM_KEY_2,                        ACTION_COLLECTING, -1
6670   },
6671   {
6672     Ykey_3_blank,                       FALSE,  FALSE,
6673     EL_EM_KEY_3,                        ACTION_COLLECTING, -1
6674   },
6675   {
6676     Ykey_4_blank,                       FALSE,  FALSE,
6677     EL_EM_KEY_4,                        ACTION_COLLECTING, -1
6678   },
6679   {
6680     Ykey_5_blank,                       FALSE,  FALSE,
6681     EL_EMC_KEY_5,                       ACTION_COLLECTING, -1
6682   },
6683   {
6684     Ykey_6_blank,                       FALSE,  FALSE,
6685     EL_EMC_KEY_6,                       ACTION_COLLECTING, -1
6686   },
6687   {
6688     Ykey_7_blank,                       FALSE,  FALSE,
6689     EL_EMC_KEY_7,                       ACTION_COLLECTING, -1
6690   },
6691   {
6692     Ykey_8_blank,                       FALSE,  FALSE,
6693     EL_EMC_KEY_8,                       ACTION_COLLECTING, -1
6694   },
6695   {
6696     Ylenses_blank,                      FALSE,  FALSE,
6697     EL_EMC_LENSES,                      ACTION_COLLECTING, -1
6698   },
6699   {
6700     Ymagnify_blank,                     FALSE,  FALSE,
6701     EL_EMC_MAGNIFIER,                   ACTION_COLLECTING, -1
6702   },
6703   {
6704     Ygrass_blank,                       FALSE,  FALSE,
6705     EL_EMC_GRASS,                       ACTION_SNAPPING, -1
6706   },
6707   {
6708     Ydirt_blank,                        FALSE,  FALSE,
6709     EL_SAND,                            ACTION_SNAPPING, -1
6710   },
6711   {
6712     Xslidewall_ns,                      TRUE,   FALSE,
6713     EL_EXPANDABLE_WALL_VERTICAL,        -1, -1
6714   },
6715   {
6716     Yslidewall_ns_blank,                FALSE,  FALSE,
6717     EL_EXPANDABLE_WALL_VERTICAL,        ACTION_GROWING, -1
6718   },
6719   {
6720     Xslidewall_ew,                      TRUE,   FALSE,
6721     EL_EXPANDABLE_WALL_HORIZONTAL,      -1, -1
6722   },
6723   {
6724     Yslidewall_ew_blank,                FALSE,  FALSE,
6725     EL_EXPANDABLE_WALL_HORIZONTAL,      ACTION_GROWING, -1
6726   },
6727   {
6728     Xwonderwall,                        TRUE,   FALSE,
6729     EL_MAGIC_WALL,                      -1, -1
6730   },
6731   {
6732     XwonderwallB,                       FALSE,  FALSE,
6733     EL_MAGIC_WALL,                      ACTION_ACTIVE, -1
6734   },
6735   {
6736     Xamoeba_1,                          TRUE,   FALSE,
6737     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6738   },
6739   {
6740     Xamoeba_2,                          FALSE,  FALSE,
6741     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6742   },
6743   {
6744     Xamoeba_3,                          FALSE,  FALSE,
6745     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6746   },
6747   {
6748     Xamoeba_4,                          FALSE,  FALSE,
6749     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
6750   },
6751   {
6752     Xamoeba_5,                          TRUE,   FALSE,
6753     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6754   },
6755   {
6756     Xamoeba_6,                          FALSE,  FALSE,
6757     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6758   },
6759   {
6760     Xamoeba_7,                          FALSE,  FALSE,
6761     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6762   },
6763   {
6764     Xamoeba_8,                          FALSE,  FALSE,
6765     EL_AMOEBA_WET,                      ACTION_OTHER, -1
6766   },
6767   {
6768     Xdoor_1,                            TRUE,   FALSE,
6769     EL_EM_GATE_1,                       -1, -1
6770   },
6771   {
6772     Xdoor_2,                            TRUE,   FALSE,
6773     EL_EM_GATE_2,                       -1, -1
6774   },
6775   {
6776     Xdoor_3,                            TRUE,   FALSE,
6777     EL_EM_GATE_3,                       -1, -1
6778   },
6779   {
6780     Xdoor_4,                            TRUE,   FALSE,
6781     EL_EM_GATE_4,                       -1, -1
6782   },
6783   {
6784     Xdoor_5,                            TRUE,   FALSE,
6785     EL_EMC_GATE_5,                      -1, -1
6786   },
6787   {
6788     Xdoor_6,                            TRUE,   FALSE,
6789     EL_EMC_GATE_6,                      -1, -1
6790   },
6791   {
6792     Xdoor_7,                            TRUE,   FALSE,
6793     EL_EMC_GATE_7,                      -1, -1
6794   },
6795   {
6796     Xdoor_8,                            TRUE,   FALSE,
6797     EL_EMC_GATE_8,                      -1, -1
6798   },
6799   {
6800     Xkey_1,                             TRUE,   FALSE,
6801     EL_EM_KEY_1,                        -1, -1
6802   },
6803   {
6804     Xkey_2,                             TRUE,   FALSE,
6805     EL_EM_KEY_2,                        -1, -1
6806   },
6807   {
6808     Xkey_3,                             TRUE,   FALSE,
6809     EL_EM_KEY_3,                        -1, -1
6810   },
6811   {
6812     Xkey_4,                             TRUE,   FALSE,
6813     EL_EM_KEY_4,                        -1, -1
6814   },
6815   {
6816     Xkey_5,                             TRUE,   FALSE,
6817     EL_EMC_KEY_5,                       -1, -1
6818   },
6819   {
6820     Xkey_6,                             TRUE,   FALSE,
6821     EL_EMC_KEY_6,                       -1, -1
6822   },
6823   {
6824     Xkey_7,                             TRUE,   FALSE,
6825     EL_EMC_KEY_7,                       -1, -1
6826   },
6827   {
6828     Xkey_8,                             TRUE,   FALSE,
6829     EL_EMC_KEY_8,                       -1, -1
6830   },
6831   {
6832     Xwind_n,                            TRUE,   FALSE,
6833     EL_BALLOON_SWITCH_UP,               -1, -1
6834   },
6835   {
6836     Xwind_e,                            TRUE,   FALSE,
6837     EL_BALLOON_SWITCH_RIGHT,            -1, -1
6838   },
6839   {
6840     Xwind_s,                            TRUE,   FALSE,
6841     EL_BALLOON_SWITCH_DOWN,             -1, -1
6842   },
6843   {
6844     Xwind_w,                            TRUE,   FALSE,
6845     EL_BALLOON_SWITCH_LEFT,             -1, -1
6846   },
6847   {
6848     Xwind_any,                          TRUE,   FALSE,
6849     EL_BALLOON_SWITCH_ANY,              -1, -1
6850   },
6851   {
6852     Xwind_stop,                         TRUE,   FALSE,
6853     EL_BALLOON_SWITCH_NONE,             -1, -1
6854   },
6855   {
6856     Xexit,                              TRUE,   FALSE,
6857     EL_EM_EXIT_CLOSED,                  -1, -1
6858   },
6859   {
6860     Xexit_1,                            TRUE,   FALSE,
6861     EL_EM_EXIT_OPEN,                    -1, -1
6862   },
6863   {
6864     Xexit_2,                            FALSE,  FALSE,
6865     EL_EM_EXIT_OPEN,                    -1, -1
6866   },
6867   {
6868     Xexit_3,                            FALSE,  FALSE,
6869     EL_EM_EXIT_OPEN,                    -1, -1
6870   },
6871   {
6872     Xdynamite,                          TRUE,   FALSE,
6873     EL_EM_DYNAMITE,                     -1, -1
6874   },
6875   {
6876     Ydynamite_blank,                    FALSE,  FALSE,
6877     EL_EM_DYNAMITE,                     ACTION_COLLECTING, -1
6878   },
6879   {
6880     Xdynamite_1,                        TRUE,   FALSE,
6881     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6882   },
6883   {
6884     Xdynamite_2,                        FALSE,  FALSE,
6885     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6886   },
6887   {
6888     Xdynamite_3,                        FALSE,  FALSE,
6889     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6890   },
6891   {
6892     Xdynamite_4,                        FALSE,  FALSE,
6893     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6894   },
6895   {
6896     Xbumper,                            TRUE,   FALSE,
6897     EL_EMC_SPRING_BUMPER,               -1, -1
6898   },
6899   {
6900     XbumperB,                           FALSE,  FALSE,
6901     EL_EMC_SPRING_BUMPER,               ACTION_ACTIVE, -1
6902   },
6903   {
6904     Xwheel,                             TRUE,   FALSE,
6905     EL_ROBOT_WHEEL,                     -1, -1
6906   },
6907   {
6908     XwheelB,                            FALSE,  FALSE,
6909     EL_ROBOT_WHEEL,                     ACTION_ACTIVE, -1
6910   },
6911   {
6912     Xswitch,                            TRUE,   FALSE,
6913     EL_EMC_MAGIC_BALL_SWITCH,           -1, -1
6914   },
6915   {
6916     XswitchB,                           FALSE,  FALSE,
6917     EL_EMC_MAGIC_BALL_SWITCH,           ACTION_ACTIVE, -1
6918   },
6919   {
6920     Xsand,                              TRUE,   FALSE,
6921     EL_QUICKSAND_EMPTY,                 -1, -1
6922   },
6923   {
6924     Xsand_stone,                        TRUE,   FALSE,
6925     EL_QUICKSAND_FULL,                  -1, -1
6926   },
6927   {
6928     Xsand_stonein_1,                    FALSE,  TRUE,
6929     EL_ROCK,                            ACTION_FILLING, -1
6930   },
6931   {
6932     Xsand_stonein_2,                    FALSE,  TRUE,
6933     EL_ROCK,                            ACTION_FILLING, -1
6934   },
6935   {
6936     Xsand_stonein_3,                    FALSE,  TRUE,
6937     EL_ROCK,                            ACTION_FILLING, -1
6938   },
6939   {
6940     Xsand_stonein_4,                    FALSE,  TRUE,
6941     EL_ROCK,                            ACTION_FILLING, -1
6942   },
6943   {
6944     Xsand_stonesand_1,                  FALSE,  FALSE,
6945     EL_QUICKSAND_EMPTYING,              -1, -1
6946   },
6947   {
6948     Xsand_stonesand_2,                  FALSE,  FALSE,
6949     EL_QUICKSAND_EMPTYING,              -1, -1
6950   },
6951   {
6952     Xsand_stonesand_3,                  FALSE,  FALSE,
6953     EL_QUICKSAND_EMPTYING,              -1, -1
6954   },
6955   {
6956     Xsand_stonesand_4,                  FALSE,  FALSE,
6957     EL_QUICKSAND_EMPTYING,              -1, -1
6958   },
6959   {
6960     Xsand_stonesand_quickout_1,         FALSE,  FALSE,
6961     EL_QUICKSAND_EMPTYING,              -1, -1
6962   },
6963   {
6964     Xsand_stonesand_quickout_2,         FALSE,  FALSE,
6965     EL_QUICKSAND_EMPTYING,              -1, -1
6966   },
6967   {
6968     Xsand_stoneout_1,                   FALSE,  FALSE,
6969     EL_ROCK,                            ACTION_EMPTYING, -1
6970   },
6971   {
6972     Xsand_stoneout_2,                   FALSE,  FALSE,
6973     EL_ROCK,                            ACTION_EMPTYING, -1
6974   },
6975   {
6976     Xsand_sandstone_1,                  FALSE,  FALSE,
6977     EL_QUICKSAND_FILLING,               -1, -1
6978   },
6979   {
6980     Xsand_sandstone_2,                  FALSE,  FALSE,
6981     EL_QUICKSAND_FILLING,               -1, -1
6982   },
6983   {
6984     Xsand_sandstone_3,                  FALSE,  FALSE,
6985     EL_QUICKSAND_FILLING,               -1, -1
6986   },
6987   {
6988     Xsand_sandstone_4,                  FALSE,  FALSE,
6989     EL_QUICKSAND_FILLING,               -1, -1
6990   },
6991   {
6992     Xplant,                             TRUE,   FALSE,
6993     EL_EMC_PLANT,                       -1, -1
6994   },
6995   {
6996     Yplant,                             FALSE,  FALSE,
6997     EL_EMC_PLANT,                       -1, -1
6998   },
6999   {
7000     Xlenses,                            TRUE,   FALSE,
7001     EL_EMC_LENSES,                      -1, -1
7002   },
7003   {
7004     Xmagnify,                           TRUE,   FALSE,
7005     EL_EMC_MAGNIFIER,                   -1, -1
7006   },
7007   {
7008     Xfake_amoeba,                       TRUE,   FALSE,
7009     EL_EMC_DRIPPER,                     -1, -1
7010   },
7011   {
7012     Xfake_amoebaB,                      FALSE,  FALSE,
7013     EL_EMC_DRIPPER,                     ACTION_ACTIVE, -1
7014   },
7015   {
7016     Xfake_blank,                        TRUE,   FALSE,
7017     EL_INVISIBLE_WALL,                  -1, -1
7018   },
7019   {
7020     Xfake_blankB,                       FALSE,  FALSE,
7021     EL_INVISIBLE_WALL,                  ACTION_ACTIVE, -1
7022   },
7023   {
7024     Xfake_grass,                        TRUE,   FALSE,
7025     EL_EMC_FAKE_GRASS,                  -1, -1
7026   },
7027   {
7028     Xfake_grassB,                       FALSE,  FALSE,
7029     EL_EMC_FAKE_GRASS,                  ACTION_ACTIVE, -1
7030   },
7031   {
7032     Xfake_door_1,                       TRUE,   FALSE,
7033     EL_EM_GATE_1_GRAY,                  -1, -1
7034   },
7035   {
7036     Xfake_door_2,                       TRUE,   FALSE,
7037     EL_EM_GATE_2_GRAY,                  -1, -1
7038   },
7039   {
7040     Xfake_door_3,                       TRUE,   FALSE,
7041     EL_EM_GATE_3_GRAY,                  -1, -1
7042   },
7043   {
7044     Xfake_door_4,                       TRUE,   FALSE,
7045     EL_EM_GATE_4_GRAY,                  -1, -1
7046   },
7047   {
7048     Xfake_door_5,                       TRUE,   FALSE,
7049     EL_EMC_GATE_5_GRAY,                 -1, -1
7050   },
7051   {
7052     Xfake_door_6,                       TRUE,   FALSE,
7053     EL_EMC_GATE_6_GRAY,                 -1, -1
7054   },
7055   {
7056     Xfake_door_7,                       TRUE,   FALSE,
7057     EL_EMC_GATE_7_GRAY,                 -1, -1
7058   },
7059   {
7060     Xfake_door_8,                       TRUE,   FALSE,
7061     EL_EMC_GATE_8_GRAY,                 -1, -1
7062   },
7063   {
7064     Xfake_acid_1,                       TRUE,   FALSE,
7065     EL_EMC_FAKE_ACID,                   -1, -1
7066   },
7067   {
7068     Xfake_acid_2,                       FALSE,  FALSE,
7069     EL_EMC_FAKE_ACID,                   -1, -1
7070   },
7071   {
7072     Xfake_acid_3,                       FALSE,  FALSE,
7073     EL_EMC_FAKE_ACID,                   -1, -1
7074   },
7075   {
7076     Xfake_acid_4,                       FALSE,  FALSE,
7077     EL_EMC_FAKE_ACID,                   -1, -1
7078   },
7079   {
7080     Xfake_acid_5,                       FALSE,  FALSE,
7081     EL_EMC_FAKE_ACID,                   -1, -1
7082   },
7083   {
7084     Xfake_acid_6,                       FALSE,  FALSE,
7085     EL_EMC_FAKE_ACID,                   -1, -1
7086   },
7087   {
7088     Xfake_acid_7,                       FALSE,  FALSE,
7089     EL_EMC_FAKE_ACID,                   -1, -1
7090   },
7091   {
7092     Xfake_acid_8,                       FALSE,  FALSE,
7093     EL_EMC_FAKE_ACID,                   -1, -1
7094   },
7095   {
7096     Xsteel_1,                           TRUE,   FALSE,
7097     EL_STEELWALL,                       -1, -1
7098   },
7099   {
7100     Xsteel_2,                           TRUE,   FALSE,
7101     EL_EMC_STEELWALL_2,                 -1, -1
7102   },
7103   {
7104     Xsteel_3,                           TRUE,   FALSE,
7105     EL_EMC_STEELWALL_3,                 -1, -1
7106   },
7107   {
7108     Xsteel_4,                           TRUE,   FALSE,
7109     EL_EMC_STEELWALL_4,                 -1, -1
7110   },
7111   {
7112     Xwall_1,                            TRUE,   FALSE,
7113     EL_WALL,                            -1, -1
7114   },
7115   {
7116     Xwall_2,                            TRUE,   FALSE,
7117     EL_EMC_WALL_14,                     -1, -1
7118   },
7119   {
7120     Xwall_3,                            TRUE,   FALSE,
7121     EL_EMC_WALL_15,                     -1, -1
7122   },
7123   {
7124     Xwall_4,                            TRUE,   FALSE,
7125     EL_EMC_WALL_16,                     -1, -1
7126   },
7127   {
7128     Xroundwall_1,                       TRUE,   FALSE,
7129     EL_WALL_SLIPPERY,                   -1, -1
7130   },
7131   {
7132     Xroundwall_2,                       TRUE,   FALSE,
7133     EL_EMC_WALL_SLIPPERY_2,             -1, -1
7134   },
7135   {
7136     Xroundwall_3,                       TRUE,   FALSE,
7137     EL_EMC_WALL_SLIPPERY_3,             -1, -1
7138   },
7139   {
7140     Xroundwall_4,                       TRUE,   FALSE,
7141     EL_EMC_WALL_SLIPPERY_4,             -1, -1
7142   },
7143   {
7144     Xdecor_1,                           TRUE,   FALSE,
7145     EL_EMC_WALL_8,                      -1, -1
7146   },
7147   {
7148     Xdecor_2,                           TRUE,   FALSE,
7149     EL_EMC_WALL_6,                      -1, -1
7150   },
7151   {
7152     Xdecor_3,                           TRUE,   FALSE,
7153     EL_EMC_WALL_4,                      -1, -1
7154   },
7155   {
7156     Xdecor_4,                           TRUE,   FALSE,
7157     EL_EMC_WALL_7,                      -1, -1
7158   },
7159   {
7160     Xdecor_5,                           TRUE,   FALSE,
7161     EL_EMC_WALL_5,                      -1, -1
7162   },
7163   {
7164     Xdecor_6,                           TRUE,   FALSE,
7165     EL_EMC_WALL_9,                      -1, -1
7166   },
7167   {
7168     Xdecor_7,                           TRUE,   FALSE,
7169     EL_EMC_WALL_10,                     -1, -1
7170   },
7171   {
7172     Xdecor_8,                           TRUE,   FALSE,
7173     EL_EMC_WALL_1,                      -1, -1
7174   },
7175   {
7176     Xdecor_9,                           TRUE,   FALSE,
7177     EL_EMC_WALL_2,                      -1, -1
7178   },
7179   {
7180     Xdecor_10,                          TRUE,   FALSE,
7181     EL_EMC_WALL_3,                      -1, -1
7182   },
7183   {
7184     Xdecor_11,                          TRUE,   FALSE,
7185     EL_EMC_WALL_11,                     -1, -1
7186   },
7187   {
7188     Xdecor_12,                          TRUE,   FALSE,
7189     EL_EMC_WALL_12,                     -1, -1
7190   },
7191   {
7192     Xalpha_0,                           TRUE,   FALSE,
7193     EL_CHAR('0'),                       -1, -1
7194   },
7195   {
7196     Xalpha_1,                           TRUE,   FALSE,
7197     EL_CHAR('1'),                       -1, -1
7198   },
7199   {
7200     Xalpha_2,                           TRUE,   FALSE,
7201     EL_CHAR('2'),                       -1, -1
7202   },
7203   {
7204     Xalpha_3,                           TRUE,   FALSE,
7205     EL_CHAR('3'),                       -1, -1
7206   },
7207   {
7208     Xalpha_4,                           TRUE,   FALSE,
7209     EL_CHAR('4'),                       -1, -1
7210   },
7211   {
7212     Xalpha_5,                           TRUE,   FALSE,
7213     EL_CHAR('5'),                       -1, -1
7214   },
7215   {
7216     Xalpha_6,                           TRUE,   FALSE,
7217     EL_CHAR('6'),                       -1, -1
7218   },
7219   {
7220     Xalpha_7,                           TRUE,   FALSE,
7221     EL_CHAR('7'),                       -1, -1
7222   },
7223   {
7224     Xalpha_8,                           TRUE,   FALSE,
7225     EL_CHAR('8'),                       -1, -1
7226   },
7227   {
7228     Xalpha_9,                           TRUE,   FALSE,
7229     EL_CHAR('9'),                       -1, -1
7230   },
7231   {
7232     Xalpha_excla,                       TRUE,   FALSE,
7233     EL_CHAR('!'),                       -1, -1
7234   },
7235   {
7236     Xalpha_quote,                       TRUE,   FALSE,
7237     EL_CHAR('"'),                       -1, -1
7238   },
7239   {
7240     Xalpha_comma,                       TRUE,   FALSE,
7241     EL_CHAR(','),                       -1, -1
7242   },
7243   {
7244     Xalpha_minus,                       TRUE,   FALSE,
7245     EL_CHAR('-'),                       -1, -1
7246   },
7247   {
7248     Xalpha_perio,                       TRUE,   FALSE,
7249     EL_CHAR('.'),                       -1, -1
7250   },
7251   {
7252     Xalpha_colon,                       TRUE,   FALSE,
7253     EL_CHAR(':'),                       -1, -1
7254   },
7255   {
7256     Xalpha_quest,                       TRUE,   FALSE,
7257     EL_CHAR('?'),                       -1, -1
7258   },
7259   {
7260     Xalpha_a,                           TRUE,   FALSE,
7261     EL_CHAR('A'),                       -1, -1
7262   },
7263   {
7264     Xalpha_b,                           TRUE,   FALSE,
7265     EL_CHAR('B'),                       -1, -1
7266   },
7267   {
7268     Xalpha_c,                           TRUE,   FALSE,
7269     EL_CHAR('C'),                       -1, -1
7270   },
7271   {
7272     Xalpha_d,                           TRUE,   FALSE,
7273     EL_CHAR('D'),                       -1, -1
7274   },
7275   {
7276     Xalpha_e,                           TRUE,   FALSE,
7277     EL_CHAR('E'),                       -1, -1
7278   },
7279   {
7280     Xalpha_f,                           TRUE,   FALSE,
7281     EL_CHAR('F'),                       -1, -1
7282   },
7283   {
7284     Xalpha_g,                           TRUE,   FALSE,
7285     EL_CHAR('G'),                       -1, -1
7286   },
7287   {
7288     Xalpha_h,                           TRUE,   FALSE,
7289     EL_CHAR('H'),                       -1, -1
7290   },
7291   {
7292     Xalpha_i,                           TRUE,   FALSE,
7293     EL_CHAR('I'),                       -1, -1
7294   },
7295   {
7296     Xalpha_j,                           TRUE,   FALSE,
7297     EL_CHAR('J'),                       -1, -1
7298   },
7299   {
7300     Xalpha_k,                           TRUE,   FALSE,
7301     EL_CHAR('K'),                       -1, -1
7302   },
7303   {
7304     Xalpha_l,                           TRUE,   FALSE,
7305     EL_CHAR('L'),                       -1, -1
7306   },
7307   {
7308     Xalpha_m,                           TRUE,   FALSE,
7309     EL_CHAR('M'),                       -1, -1
7310   },
7311   {
7312     Xalpha_n,                           TRUE,   FALSE,
7313     EL_CHAR('N'),                       -1, -1
7314   },
7315   {
7316     Xalpha_o,                           TRUE,   FALSE,
7317     EL_CHAR('O'),                       -1, -1
7318   },
7319   {
7320     Xalpha_p,                           TRUE,   FALSE,
7321     EL_CHAR('P'),                       -1, -1
7322   },
7323   {
7324     Xalpha_q,                           TRUE,   FALSE,
7325     EL_CHAR('Q'),                       -1, -1
7326   },
7327   {
7328     Xalpha_r,                           TRUE,   FALSE,
7329     EL_CHAR('R'),                       -1, -1
7330   },
7331   {
7332     Xalpha_s,                           TRUE,   FALSE,
7333     EL_CHAR('S'),                       -1, -1
7334   },
7335   {
7336     Xalpha_t,                           TRUE,   FALSE,
7337     EL_CHAR('T'),                       -1, -1
7338   },
7339   {
7340     Xalpha_u,                           TRUE,   FALSE,
7341     EL_CHAR('U'),                       -1, -1
7342   },
7343   {
7344     Xalpha_v,                           TRUE,   FALSE,
7345     EL_CHAR('V'),                       -1, -1
7346   },
7347   {
7348     Xalpha_w,                           TRUE,   FALSE,
7349     EL_CHAR('W'),                       -1, -1
7350   },
7351   {
7352     Xalpha_x,                           TRUE,   FALSE,
7353     EL_CHAR('X'),                       -1, -1
7354   },
7355   {
7356     Xalpha_y,                           TRUE,   FALSE,
7357     EL_CHAR('Y'),                       -1, -1
7358   },
7359   {
7360     Xalpha_z,                           TRUE,   FALSE,
7361     EL_CHAR('Z'),                       -1, -1
7362   },
7363   {
7364     Xalpha_arrow_e,                     TRUE,   FALSE,
7365     EL_CHAR('>'),                       -1, -1
7366   },
7367   {
7368     Xalpha_arrow_w,                     TRUE,   FALSE,
7369     EL_CHAR('<'),                       -1, -1
7370   },
7371   {
7372     Xalpha_copyr,                       TRUE,   FALSE,
7373     EL_CHAR(CHAR_BYTE_COPYRIGHT),       -1, -1
7374   },
7375
7376   {
7377     Xboom_bug,                          FALSE,  FALSE,
7378     EL_BUG,                             ACTION_EXPLODING, -1
7379   },
7380   {
7381     Xboom_bomb,                         FALSE,  FALSE,
7382     EL_BOMB,                            ACTION_EXPLODING, -1
7383   },
7384   {
7385     Xboom_android,                      FALSE,  FALSE,
7386     EL_EMC_ANDROID,                     ACTION_OTHER, -1
7387   },
7388   {
7389     Xboom_1,                            FALSE,  FALSE,
7390     EL_DEFAULT,                         ACTION_EXPLODING, -1
7391   },
7392   {
7393     Xboom_2,                            FALSE,  FALSE,
7394     EL_DEFAULT,                         ACTION_EXPLODING, -1
7395   },
7396   {
7397     Znormal,                            FALSE,  FALSE,
7398     EL_EMPTY,                           -1, -1
7399   },
7400   {
7401     Zdynamite,                          FALSE,  FALSE,
7402     EL_EMPTY,                           -1, -1
7403   },
7404   {
7405     Zplayer,                            FALSE,  FALSE,
7406     EL_EMPTY,                           -1, -1
7407   },
7408   {
7409     Zborder,                            FALSE,  FALSE,
7410     EL_EMPTY,                           -1, -1
7411   },
7412
7413   {
7414     -1,                                 FALSE,  FALSE,
7415     -1,                                 -1, -1
7416   }
7417 };
7418
7419 static struct Mapping_EM_to_RND_player
7420 {
7421   int action_em;
7422   int player_nr;
7423
7424   int element_rnd;
7425   int action;
7426   int direction;
7427 }
7428 em_player_mapping_list[] =
7429 {
7430   {
7431     PLY_walk_n,                         0,
7432     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_UP,
7433   },
7434   {
7435     PLY_walk_e,                         0,
7436     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_RIGHT,
7437   },
7438   {
7439     PLY_walk_s,                         0,
7440     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_DOWN,
7441   },
7442   {
7443     PLY_walk_w,                         0,
7444     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_LEFT,
7445   },
7446   {
7447     PLY_push_n,                         0,
7448     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_UP,
7449   },
7450   {
7451     PLY_push_e,                         0,
7452     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_RIGHT,
7453   },
7454   {
7455     PLY_push_s,                         0,
7456     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_DOWN,
7457   },
7458   {
7459     PLY_push_w,                         0,
7460     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_LEFT,
7461   },
7462   {
7463     PLY_shoot_n,                        0,
7464     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_UP,
7465   },
7466   {
7467     PLY_shoot_e,                        0,
7468     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7469   },
7470   {
7471     PLY_shoot_s,                        0,
7472     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_DOWN,
7473   },
7474   {
7475     PLY_shoot_w,                        0,
7476     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_LEFT,
7477   },
7478   {
7479     PLY_walk_n,                         1,
7480     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_UP,
7481   },
7482   {
7483     PLY_walk_e,                         1,
7484     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_RIGHT,
7485   },
7486   {
7487     PLY_walk_s,                         1,
7488     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_DOWN,
7489   },
7490   {
7491     PLY_walk_w,                         1,
7492     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_LEFT,
7493   },
7494   {
7495     PLY_push_n,                         1,
7496     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_UP,
7497   },
7498   {
7499     PLY_push_e,                         1,
7500     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_RIGHT,
7501   },
7502   {
7503     PLY_push_s,                         1,
7504     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_DOWN,
7505   },
7506   {
7507     PLY_push_w,                         1,
7508     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_LEFT,
7509   },
7510   {
7511     PLY_shoot_n,                        1,
7512     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_UP,
7513   },
7514   {
7515     PLY_shoot_e,                        1,
7516     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7517   },
7518   {
7519     PLY_shoot_s,                        1,
7520     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_DOWN,
7521   },
7522   {
7523     PLY_shoot_w,                        1,
7524     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_LEFT,
7525   },
7526   {
7527     PLY_still,                          0,
7528     EL_PLAYER_1,                        ACTION_DEFAULT, -1,
7529   },
7530   {
7531     PLY_still,                          1,
7532     EL_PLAYER_2,                        ACTION_DEFAULT, -1,
7533   },
7534   {
7535     PLY_walk_n,                         2,
7536     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_UP,
7537   },
7538   {
7539     PLY_walk_e,                         2,
7540     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_RIGHT,
7541   },
7542   {
7543     PLY_walk_s,                         2,
7544     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_DOWN,
7545   },
7546   {
7547     PLY_walk_w,                         2,
7548     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_LEFT,
7549   },
7550   {
7551     PLY_push_n,                         2,
7552     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_UP,
7553   },
7554   {
7555     PLY_push_e,                         2,
7556     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_RIGHT,
7557   },
7558   {
7559     PLY_push_s,                         2,
7560     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_DOWN,
7561   },
7562   {
7563     PLY_push_w,                         2,
7564     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_LEFT,
7565   },
7566   {
7567     PLY_shoot_n,                        2,
7568     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_UP,
7569   },
7570   {
7571     PLY_shoot_e,                        2,
7572     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7573   },
7574   {
7575     PLY_shoot_s,                        2,
7576     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_DOWN,
7577   },
7578   {
7579     PLY_shoot_w,                        2,
7580     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_LEFT,
7581   },
7582   {
7583     PLY_walk_n,                         3,
7584     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_UP,
7585   },
7586   {
7587     PLY_walk_e,                         3,
7588     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_RIGHT,
7589   },
7590   {
7591     PLY_walk_s,                         3,
7592     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_DOWN,
7593   },
7594   {
7595     PLY_walk_w,                         3,
7596     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_LEFT,
7597   },
7598   {
7599     PLY_push_n,                         3,
7600     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_UP,
7601   },
7602   {
7603     PLY_push_e,                         3,
7604     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_RIGHT,
7605   },
7606   {
7607     PLY_push_s,                         3,
7608     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_DOWN,
7609   },
7610   {
7611     PLY_push_w,                         3,
7612     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_LEFT,
7613   },
7614   {
7615     PLY_shoot_n,                        3,
7616     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_UP,
7617   },
7618   {
7619     PLY_shoot_e,                        3,
7620     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7621   },
7622   {
7623     PLY_shoot_s,                        3,
7624     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_DOWN,
7625   },
7626   {
7627     PLY_shoot_w,                        3,
7628     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_LEFT,
7629   },
7630   {
7631     PLY_still,                          2,
7632     EL_PLAYER_3,                        ACTION_DEFAULT, -1,
7633   },
7634   {
7635     PLY_still,                          3,
7636     EL_PLAYER_4,                        ACTION_DEFAULT, -1,
7637   },
7638
7639   {
7640     -1,                                 -1,
7641     -1,                                 -1, -1
7642   }
7643 };
7644
7645 int map_element_RND_to_EM(int element_rnd)
7646 {
7647   static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7648   static boolean mapping_initialized = FALSE;
7649
7650   if (!mapping_initialized)
7651   {
7652     int i;
7653
7654     // return "Xalpha_quest" for all undefined elements in mapping array
7655     for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7656       mapping_RND_to_EM[i] = Xalpha_quest;
7657
7658     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7659       if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7660         mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7661           em_object_mapping_list[i].element_em;
7662
7663     mapping_initialized = TRUE;
7664   }
7665
7666   if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7667     return mapping_RND_to_EM[element_rnd];
7668
7669   Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7670
7671   return EL_UNKNOWN;
7672 }
7673
7674 int map_element_EM_to_RND(int element_em)
7675 {
7676   static unsigned short mapping_EM_to_RND[TILE_MAX];
7677   static boolean mapping_initialized = FALSE;
7678
7679   if (!mapping_initialized)
7680   {
7681     int i;
7682
7683     // return "EL_UNKNOWN" for all undefined elements in mapping array
7684     for (i = 0; i < TILE_MAX; i++)
7685       mapping_EM_to_RND[i] = EL_UNKNOWN;
7686
7687     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7688       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7689         em_object_mapping_list[i].element_rnd;
7690
7691     mapping_initialized = TRUE;
7692   }
7693
7694   if (element_em >= 0 && element_em < TILE_MAX)
7695     return mapping_EM_to_RND[element_em];
7696
7697   Error(ERR_WARN, "invalid EM level element %d", element_em);
7698
7699   return EL_UNKNOWN;
7700 }
7701
7702 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7703 {
7704   struct LevelInfo_EM *level_em = level->native_em_level;
7705   struct LEVEL *lev = level_em->lev;
7706   int i, j;
7707
7708   for (i = 0; i < TILE_MAX; i++)
7709     lev->android_array[i] = Xblank;
7710
7711   for (i = 0; i < level->num_android_clone_elements; i++)
7712   {
7713     int element_rnd = level->android_clone_element[i];
7714     int element_em = map_element_RND_to_EM(element_rnd);
7715
7716     for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7717       if (em_object_mapping_list[j].element_rnd == element_rnd)
7718         lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7719   }
7720 }
7721
7722 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7723 {
7724   struct LevelInfo_EM *level_em = level->native_em_level;
7725   struct LEVEL *lev = level_em->lev;
7726   int i, j;
7727
7728   level->num_android_clone_elements = 0;
7729
7730   for (i = 0; i < TILE_MAX; i++)
7731   {
7732     int element_em = lev->android_array[i];
7733     int element_rnd;
7734     boolean element_found = FALSE;
7735
7736     if (element_em == Xblank)
7737       continue;
7738
7739     element_rnd = map_element_EM_to_RND(element_em);
7740
7741     for (j = 0; j < level->num_android_clone_elements; j++)
7742       if (level->android_clone_element[j] == element_rnd)
7743         element_found = TRUE;
7744
7745     if (!element_found)
7746     {
7747       level->android_clone_element[level->num_android_clone_elements++] =
7748         element_rnd;
7749
7750       if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7751         break;
7752     }
7753   }
7754
7755   if (level->num_android_clone_elements == 0)
7756   {
7757     level->num_android_clone_elements = 1;
7758     level->android_clone_element[0] = EL_EMPTY;
7759   }
7760 }
7761
7762 int map_direction_RND_to_EM(int direction)
7763 {
7764   return (direction == MV_UP    ? 0 :
7765           direction == MV_RIGHT ? 1 :
7766           direction == MV_DOWN  ? 2 :
7767           direction == MV_LEFT  ? 3 :
7768           -1);
7769 }
7770
7771 int map_direction_EM_to_RND(int direction)
7772 {
7773   return (direction == 0 ? MV_UP    :
7774           direction == 1 ? MV_RIGHT :
7775           direction == 2 ? MV_DOWN  :
7776           direction == 3 ? MV_LEFT  :
7777           MV_NONE);
7778 }
7779
7780 int map_element_RND_to_SP(int element_rnd)
7781 {
7782   int element_sp = 0x20;        // map unknown elements to yellow "hardware"
7783
7784   if (element_rnd >= EL_SP_START &&
7785       element_rnd <= EL_SP_END)
7786     element_sp = element_rnd - EL_SP_START;
7787   else if (element_rnd == EL_EMPTY_SPACE)
7788     element_sp = 0x00;
7789   else if (element_rnd == EL_INVISIBLE_WALL)
7790     element_sp = 0x28;
7791
7792   return element_sp;
7793 }
7794
7795 int map_element_SP_to_RND(int element_sp)
7796 {
7797   int element_rnd = EL_UNKNOWN;
7798
7799   if (element_sp >= 0x00 &&
7800       element_sp <= 0x27)
7801     element_rnd = EL_SP_START + element_sp;
7802   else if (element_sp == 0x28)
7803     element_rnd = EL_INVISIBLE_WALL;
7804
7805   return element_rnd;
7806 }
7807
7808 int map_action_SP_to_RND(int action_sp)
7809 {
7810   switch (action_sp)
7811   {
7812     case actActive:             return ACTION_ACTIVE;
7813     case actImpact:             return ACTION_IMPACT;
7814     case actExploding:          return ACTION_EXPLODING;
7815     case actDigging:            return ACTION_DIGGING;
7816     case actSnapping:           return ACTION_SNAPPING;
7817     case actCollecting:         return ACTION_COLLECTING;
7818     case actPassing:            return ACTION_PASSING;
7819     case actPushing:            return ACTION_PUSHING;
7820     case actDropping:           return ACTION_DROPPING;
7821
7822     default:                    return ACTION_DEFAULT;
7823   }
7824 }
7825
7826 int map_element_RND_to_MM(int element_rnd)
7827 {
7828   return (element_rnd >= EL_MM_START_1 &&
7829           element_rnd <= EL_MM_END_1 ?
7830           EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7831
7832           element_rnd >= EL_MM_START_2 &&
7833           element_rnd <= EL_MM_END_2 ?
7834           EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7835
7836           element_rnd >= EL_CHAR_START &&
7837           element_rnd <= EL_CHAR_END ?
7838           EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7839
7840           element_rnd >= EL_MM_RUNTIME_START &&
7841           element_rnd <= EL_MM_RUNTIME_END ?
7842           EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7843
7844           element_rnd >= EL_MM_DUMMY_START &&
7845           element_rnd <= EL_MM_DUMMY_END ?
7846           EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7847
7848           EL_MM_EMPTY_NATIVE);
7849 }
7850
7851 int map_element_MM_to_RND(int element_mm)
7852 {
7853   return (element_mm == EL_MM_EMPTY_NATIVE ||
7854           element_mm == EL_DF_EMPTY_NATIVE ?
7855           EL_EMPTY :
7856
7857           element_mm >= EL_MM_START_1_NATIVE &&
7858           element_mm <= EL_MM_END_1_NATIVE ?
7859           EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7860
7861           element_mm >= EL_MM_START_2_NATIVE &&
7862           element_mm <= EL_MM_END_2_NATIVE ?
7863           EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7864
7865           element_mm >= EL_MM_CHAR_START_NATIVE &&
7866           element_mm <= EL_MM_CHAR_END_NATIVE ?
7867           EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7868
7869           element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7870           element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7871           EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7872
7873           element_mm >= EL_MM_DUMMY_START_NATIVE &&
7874           element_mm <= EL_MM_DUMMY_END_NATIVE ?
7875           EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7876
7877           EL_EMPTY);
7878 }
7879
7880 int map_action_MM_to_RND(int action_mm)
7881 {
7882   // all MM actions are defined to exactly match their RND counterparts
7883   return action_mm;
7884 }
7885
7886 int map_sound_MM_to_RND(int sound_mm)
7887 {
7888   switch (sound_mm)
7889   {
7890     case SND_MM_GAME_LEVELTIME_CHARGING:
7891       return SND_GAME_LEVELTIME_CHARGING;
7892
7893     case SND_MM_GAME_HEALTH_CHARGING:
7894       return SND_GAME_HEALTH_CHARGING;
7895
7896     default:
7897       return SND_UNDEFINED;
7898   }
7899 }
7900
7901 int map_mm_wall_element(int element)
7902 {
7903   return (element >= EL_MM_STEEL_WALL_START &&
7904           element <= EL_MM_STEEL_WALL_END ?
7905           EL_MM_STEEL_WALL :
7906
7907           element >= EL_MM_WOODEN_WALL_START &&
7908           element <= EL_MM_WOODEN_WALL_END ?
7909           EL_MM_WOODEN_WALL :
7910
7911           element >= EL_MM_ICE_WALL_START &&
7912           element <= EL_MM_ICE_WALL_END ?
7913           EL_MM_ICE_WALL :
7914
7915           element >= EL_MM_AMOEBA_WALL_START &&
7916           element <= EL_MM_AMOEBA_WALL_END ?
7917           EL_MM_AMOEBA_WALL :
7918
7919           element >= EL_DF_STEEL_WALL_START &&
7920           element <= EL_DF_STEEL_WALL_END ?
7921           EL_DF_STEEL_WALL :
7922
7923           element >= EL_DF_WOODEN_WALL_START &&
7924           element <= EL_DF_WOODEN_WALL_END ?
7925           EL_DF_WOODEN_WALL :
7926
7927           element);
7928 }
7929
7930 int map_mm_wall_element_editor(int element)
7931 {
7932   switch (element)
7933   {
7934     case EL_MM_STEEL_WALL:      return EL_MM_STEEL_WALL_START;
7935     case EL_MM_WOODEN_WALL:     return EL_MM_WOODEN_WALL_START;
7936     case EL_MM_ICE_WALL:        return EL_MM_ICE_WALL_START;
7937     case EL_MM_AMOEBA_WALL:     return EL_MM_AMOEBA_WALL_START;
7938     case EL_DF_STEEL_WALL:      return EL_DF_STEEL_WALL_START;
7939     case EL_DF_WOODEN_WALL:     return EL_DF_WOODEN_WALL_START;
7940
7941     default:                    return element;
7942   }
7943 }
7944
7945 int get_next_element(int element)
7946 {
7947   switch (element)
7948   {
7949     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
7950     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
7951     case EL_QUICKSAND_FAST_FILLING:     return EL_QUICKSAND_FAST_FULL;
7952     case EL_QUICKSAND_FAST_EMPTYING:    return EL_QUICKSAND_FAST_EMPTY;
7953     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
7954     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
7955     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
7956     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
7957     case EL_DC_MAGIC_WALL_FILLING:      return EL_DC_MAGIC_WALL_FULL;
7958     case EL_DC_MAGIC_WALL_EMPTYING:     return EL_DC_MAGIC_WALL_ACTIVE;
7959     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
7960
7961     default:                            return element;
7962   }
7963 }
7964
7965 int el2img_mm(int element_mm)
7966 {
7967   return el2img(map_element_MM_to_RND(element_mm));
7968 }
7969
7970 int el_act_dir2img(int element, int action, int direction)
7971 {
7972   element = GFX_ELEMENT(element);
7973   direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7974
7975   // direction_graphic[][] == graphic[] for undefined direction graphics
7976   return element_info[element].direction_graphic[action][direction];
7977 }
7978
7979 static int el_act_dir2crm(int element, int action, int direction)
7980 {
7981   element = GFX_ELEMENT(element);
7982   direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7983
7984   // direction_graphic[][] == graphic[] for undefined direction graphics
7985   return element_info[element].direction_crumbled[action][direction];
7986 }
7987
7988 int el_act2img(int element, int action)
7989 {
7990   element = GFX_ELEMENT(element);
7991
7992   return element_info[element].graphic[action];
7993 }
7994
7995 int el_act2crm(int element, int action)
7996 {
7997   element = GFX_ELEMENT(element);
7998
7999   return element_info[element].crumbled[action];
8000 }
8001
8002 int el_dir2img(int element, int direction)
8003 {
8004   element = GFX_ELEMENT(element);
8005
8006   return el_act_dir2img(element, ACTION_DEFAULT, direction);
8007 }
8008
8009 int el2baseimg(int element)
8010 {
8011   return element_info[element].graphic[ACTION_DEFAULT];
8012 }
8013
8014 int el2img(int element)
8015 {
8016   element = GFX_ELEMENT(element);
8017
8018   return element_info[element].graphic[ACTION_DEFAULT];
8019 }
8020
8021 int el2edimg(int element)
8022 {
8023   element = GFX_ELEMENT(element);
8024
8025   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8026 }
8027
8028 int el2preimg(int element)
8029 {
8030   element = GFX_ELEMENT(element);
8031
8032   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8033 }
8034
8035 int el2panelimg(int element)
8036 {
8037   element = GFX_ELEMENT(element);
8038
8039   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8040 }
8041
8042 int font2baseimg(int font_nr)
8043 {
8044   return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8045 }
8046
8047 int getBeltNrFromBeltElement(int element)
8048 {
8049   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8050           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8051           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8052 }
8053
8054 int getBeltNrFromBeltActiveElement(int element)
8055 {
8056   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8057           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8058           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8059 }
8060
8061 int getBeltNrFromBeltSwitchElement(int element)
8062 {
8063   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8064           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8065           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8066 }
8067
8068 int getBeltDirNrFromBeltElement(int element)
8069 {
8070   static int belt_base_element[4] =
8071   {
8072     EL_CONVEYOR_BELT_1_LEFT,
8073     EL_CONVEYOR_BELT_2_LEFT,
8074     EL_CONVEYOR_BELT_3_LEFT,
8075     EL_CONVEYOR_BELT_4_LEFT
8076   };
8077
8078   int belt_nr = getBeltNrFromBeltElement(element);
8079   int belt_dir_nr = element - belt_base_element[belt_nr];
8080
8081   return (belt_dir_nr % 3);
8082 }
8083
8084 int getBeltDirNrFromBeltSwitchElement(int element)
8085 {
8086   static int belt_base_element[4] =
8087   {
8088     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8089     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8090     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8091     EL_CONVEYOR_BELT_4_SWITCH_LEFT
8092   };
8093
8094   int belt_nr = getBeltNrFromBeltSwitchElement(element);
8095   int belt_dir_nr = element - belt_base_element[belt_nr];
8096
8097   return (belt_dir_nr % 3);
8098 }
8099
8100 int getBeltDirFromBeltElement(int element)
8101 {
8102   static int belt_move_dir[3] =
8103   {
8104     MV_LEFT,
8105     MV_NONE,
8106     MV_RIGHT
8107   };
8108
8109   int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8110
8111   return belt_move_dir[belt_dir_nr];
8112 }
8113
8114 int getBeltDirFromBeltSwitchElement(int element)
8115 {
8116   static int belt_move_dir[3] =
8117   {
8118     MV_LEFT,
8119     MV_NONE,
8120     MV_RIGHT
8121   };
8122
8123   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8124
8125   return belt_move_dir[belt_dir_nr];
8126 }
8127
8128 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8129 {
8130   static int belt_base_element[4] =
8131   {
8132     EL_CONVEYOR_BELT_1_LEFT,
8133     EL_CONVEYOR_BELT_2_LEFT,
8134     EL_CONVEYOR_BELT_3_LEFT,
8135     EL_CONVEYOR_BELT_4_LEFT
8136   };
8137
8138   return belt_base_element[belt_nr] + belt_dir_nr;
8139 }
8140
8141 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8142 {
8143   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8144
8145   return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8146 }
8147
8148 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8149 {
8150   static int belt_base_element[4] =
8151   {
8152     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8153     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8154     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8155     EL_CONVEYOR_BELT_4_SWITCH_LEFT
8156   };
8157
8158   return belt_base_element[belt_nr] + belt_dir_nr;
8159 }
8160
8161 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8162 {
8163   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8164
8165   return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8166 }
8167
8168 boolean getTeamMode_EM(void)
8169 {
8170   return game.team_mode || network_playing;
8171 }
8172
8173 int getGameFrameDelay_EM(int native_em_game_frame_delay)
8174 {
8175   int game_frame_delay_value;
8176
8177   game_frame_delay_value =
8178     (tape.playing && tape.fast_forward ? FfwdFrameDelay :
8179      GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
8180      GameFrameDelay);
8181
8182   if (tape.playing && tape.warp_forward && !tape.pausing)
8183     game_frame_delay_value = 0;
8184
8185   return game_frame_delay_value;
8186 }
8187
8188 unsigned int InitRND(int seed)
8189 {
8190   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8191     return InitEngineRandom_EM(seed);
8192   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8193     return InitEngineRandom_SP(seed);
8194   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8195     return InitEngineRandom_MM(seed);
8196   else
8197     return InitEngineRandom_RND(seed);
8198 }
8199
8200 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
8201 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8202
8203 static int get_effective_element_EM(int tile, int frame_em)
8204 {
8205   int element             = object_mapping[tile].element_rnd;
8206   int action              = object_mapping[tile].action;
8207   boolean is_backside     = object_mapping[tile].is_backside;
8208   boolean action_removing = (action == ACTION_DIGGING ||
8209                              action == ACTION_SNAPPING ||
8210                              action == ACTION_COLLECTING);
8211
8212   if (frame_em < 7)
8213   {
8214     switch (tile)
8215     {
8216       case Xacid_splash_e:
8217       case Xacid_splash_w:
8218         return (frame_em > 5 ? EL_EMPTY : element);
8219
8220       default:
8221         return element;
8222     }
8223   }
8224   else  // frame_em == 7
8225   {
8226     switch (tile)
8227     {
8228       case Xacid_splash_e:
8229       case Xacid_splash_w:
8230         return EL_EMPTY;
8231
8232       case Ynut_stone:
8233         return EL_EMERALD;
8234
8235       case Ydiamond_stone:
8236         return EL_ROCK;
8237
8238       case Xdrip_stretch:
8239       case Xdrip_stretchB:
8240       case Ydrip_1_s:
8241       case Ydrip_1_sB:
8242       case Yball_1:
8243       case Xball_2:
8244       case Yball_2:
8245       case Yball_blank:
8246       case Ykey_1_blank:
8247       case Ykey_2_blank:
8248       case Ykey_3_blank:
8249       case Ykey_4_blank:
8250       case Ykey_5_blank:
8251       case Ykey_6_blank:
8252       case Ykey_7_blank:
8253       case Ykey_8_blank:
8254       case Ylenses_blank:
8255       case Ymagnify_blank:
8256       case Ygrass_blank:
8257       case Ydirt_blank:
8258       case Xsand_stonein_1:
8259       case Xsand_stonein_2:
8260       case Xsand_stonein_3:
8261       case Xsand_stonein_4:
8262         return element;
8263
8264       default:
8265         return (is_backside || action_removing ? EL_EMPTY : element);
8266     }
8267   }
8268 }
8269
8270 static boolean check_linear_animation_EM(int tile)
8271 {
8272   switch (tile)
8273   {
8274     case Xsand_stonesand_1:
8275     case Xsand_stonesand_quickout_1:
8276     case Xsand_sandstone_1:
8277     case Xsand_stonein_1:
8278     case Xsand_stoneout_1:
8279     case Xboom_1:
8280     case Xdynamite_1:
8281     case Ybug_w_n:
8282     case Ybug_n_e:
8283     case Ybug_e_s:
8284     case Ybug_s_w:
8285     case Ybug_e_n:
8286     case Ybug_s_e:
8287     case Ybug_w_s:
8288     case Ybug_n_w:
8289     case Ytank_w_n:
8290     case Ytank_n_e:
8291     case Ytank_e_s:
8292     case Ytank_s_w:
8293     case Ytank_e_n:
8294     case Ytank_s_e:
8295     case Ytank_w_s:
8296     case Ytank_n_w:
8297     case Xacid_splash_e:
8298     case Xacid_splash_w:
8299     case Ynut_stone:
8300       return TRUE;
8301   }
8302
8303   return FALSE;
8304 }
8305
8306 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8307                                      boolean has_crumbled_graphics,
8308                                      int crumbled, int sync_frame)
8309 {
8310   // if element can be crumbled, but certain action graphics are just empty
8311   // space (like instantly snapping sand to empty space in 1 frame), do not
8312   // treat these empty space graphics as crumbled graphics in EMC engine
8313   if (crumbled == IMG_EMPTY_SPACE)
8314     has_crumbled_graphics = FALSE;
8315
8316   if (has_crumbled_graphics)
8317   {
8318     struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8319     int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8320                                            g_crumbled->anim_delay,
8321                                            g_crumbled->anim_mode,
8322                                            g_crumbled->anim_start_frame,
8323                                            sync_frame);
8324
8325     getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8326                      &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8327
8328     g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8329     g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8330
8331     g_em->has_crumbled_graphics = TRUE;
8332   }
8333   else
8334   {
8335     g_em->crumbled_bitmap = NULL;
8336     g_em->crumbled_src_x = 0;
8337     g_em->crumbled_src_y = 0;
8338     g_em->crumbled_border_size = 0;
8339     g_em->crumbled_tile_size = 0;
8340
8341     g_em->has_crumbled_graphics = FALSE;
8342   }
8343 }
8344
8345 #if 0
8346 void ResetGfxAnimation_EM(int x, int y, int tile)
8347 {
8348   GfxFrame[x][y] = 0;
8349 }
8350 #endif
8351
8352 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8353                         int tile, int frame_em, int x, int y)
8354 {
8355   int action = object_mapping[tile].action;
8356   int direction = object_mapping[tile].direction;
8357   int effective_element = get_effective_element_EM(tile, frame_em);
8358   int graphic = (direction == MV_NONE ?
8359                  el_act2img(effective_element, action) :
8360                  el_act_dir2img(effective_element, action, direction));
8361   struct GraphicInfo *g = &graphic_info[graphic];
8362   int sync_frame;
8363   boolean action_removing = (action == ACTION_DIGGING ||
8364                              action == ACTION_SNAPPING ||
8365                              action == ACTION_COLLECTING);
8366   boolean action_moving   = (action == ACTION_FALLING ||
8367                              action == ACTION_MOVING ||
8368                              action == ACTION_PUSHING ||
8369                              action == ACTION_EATING ||
8370                              action == ACTION_FILLING ||
8371                              action == ACTION_EMPTYING);
8372   boolean action_falling  = (action == ACTION_FALLING ||
8373                              action == ACTION_FILLING ||
8374                              action == ACTION_EMPTYING);
8375
8376   // special case: graphic uses "2nd movement tile" and has defined
8377   // 7 frames for movement animation (or less) => use default graphic
8378   // for last (8th) frame which ends the movement animation
8379   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8380   {
8381     action = ACTION_DEFAULT;    // (keep action_* unchanged for now)
8382     graphic = (direction == MV_NONE ?
8383                el_act2img(effective_element, action) :
8384                el_act_dir2img(effective_element, action, direction));
8385
8386     g = &graphic_info[graphic];
8387   }
8388
8389   if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8390   {
8391     GfxFrame[x][y] = 0;
8392   }
8393   else if (action_moving)
8394   {
8395     boolean is_backside = object_mapping[tile].is_backside;
8396
8397     if (is_backside)
8398     {
8399       int direction = object_mapping[tile].direction;
8400       int move_dir = (action_falling ? MV_DOWN : direction);
8401
8402       GfxFrame[x][y]++;
8403
8404 #if 1
8405       // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8406       if (g->double_movement && frame_em == 0)
8407         GfxFrame[x][y] = 0;
8408 #endif
8409
8410       if (move_dir == MV_LEFT)
8411         GfxFrame[x - 1][y] = GfxFrame[x][y];
8412       else if (move_dir == MV_RIGHT)
8413         GfxFrame[x + 1][y] = GfxFrame[x][y];
8414       else if (move_dir == MV_UP)
8415         GfxFrame[x][y - 1] = GfxFrame[x][y];
8416       else if (move_dir == MV_DOWN)
8417         GfxFrame[x][y + 1] = GfxFrame[x][y];
8418     }
8419   }
8420   else
8421   {
8422     GfxFrame[x][y]++;
8423
8424     // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8425     if (tile == Xsand_stonesand_quickout_1 ||
8426         tile == Xsand_stonesand_quickout_2)
8427       GfxFrame[x][y]++;
8428   }
8429
8430   if (graphic_info[graphic].anim_global_sync)
8431     sync_frame = FrameCounter;
8432   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8433     sync_frame = GfxFrame[x][y];
8434   else
8435     sync_frame = 0;     // playfield border (pseudo steel)
8436
8437   SetRandomAnimationValue(x, y);
8438
8439   int frame = getAnimationFrame(g->anim_frames,
8440                                 g->anim_delay,
8441                                 g->anim_mode,
8442                                 g->anim_start_frame,
8443                                 sync_frame);
8444
8445   g_em->unique_identifier =
8446     (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8447 }
8448
8449 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8450                                   int tile, int frame_em, int x, int y)
8451 {
8452   int action = object_mapping[tile].action;
8453   int direction = object_mapping[tile].direction;
8454   boolean is_backside = object_mapping[tile].is_backside;
8455   int effective_element = get_effective_element_EM(tile, frame_em);
8456   int effective_action = action;
8457   int graphic = (direction == MV_NONE ?
8458                  el_act2img(effective_element, effective_action) :
8459                  el_act_dir2img(effective_element, effective_action,
8460                                 direction));
8461   int crumbled = (direction == MV_NONE ?
8462                   el_act2crm(effective_element, effective_action) :
8463                   el_act_dir2crm(effective_element, effective_action,
8464                                  direction));
8465   int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8466   int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8467   boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8468   struct GraphicInfo *g = &graphic_info[graphic];
8469   int sync_frame;
8470
8471   // special case: graphic uses "2nd movement tile" and has defined
8472   // 7 frames for movement animation (or less) => use default graphic
8473   // for last (8th) frame which ends the movement animation
8474   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8475   {
8476     effective_action = ACTION_DEFAULT;
8477     graphic = (direction == MV_NONE ?
8478                el_act2img(effective_element, effective_action) :
8479                el_act_dir2img(effective_element, effective_action,
8480                               direction));
8481     crumbled = (direction == MV_NONE ?
8482                 el_act2crm(effective_element, effective_action) :
8483                 el_act_dir2crm(effective_element, effective_action,
8484                                direction));
8485
8486     g = &graphic_info[graphic];
8487   }
8488
8489   if (graphic_info[graphic].anim_global_sync)
8490     sync_frame = FrameCounter;
8491   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8492     sync_frame = GfxFrame[x][y];
8493   else
8494     sync_frame = 0;     // playfield border (pseudo steel)
8495
8496   SetRandomAnimationValue(x, y);
8497
8498   int frame = getAnimationFrame(g->anim_frames,
8499                                 g->anim_delay,
8500                                 g->anim_mode,
8501                                 g->anim_start_frame,
8502                                 sync_frame);
8503
8504   getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8505                       g->double_movement && is_backside);
8506
8507   // (updating the "crumbled" graphic definitions is probably not really needed,
8508   // as animations for crumbled graphics can't be longer than one EMC cycle)
8509   set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8510                            sync_frame);
8511 }
8512
8513 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8514                                   int player_nr, int anim, int frame_em)
8515 {
8516   int element   = player_mapping[player_nr][anim].element_rnd;
8517   int action    = player_mapping[player_nr][anim].action;
8518   int direction = player_mapping[player_nr][anim].direction;
8519   int graphic = (direction == MV_NONE ?
8520                  el_act2img(element, action) :
8521                  el_act_dir2img(element, action, direction));
8522   struct GraphicInfo *g = &graphic_info[graphic];
8523   int sync_frame;
8524
8525   InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8526
8527   stored_player[player_nr].StepFrame = frame_em;
8528
8529   sync_frame = stored_player[player_nr].Frame;
8530
8531   int frame = getAnimationFrame(g->anim_frames,
8532                                 g->anim_delay,
8533                                 g->anim_mode,
8534                                 g->anim_start_frame,
8535                                 sync_frame);
8536
8537   getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8538                       &g_em->src_x, &g_em->src_y, FALSE);
8539 }
8540
8541 void InitGraphicInfo_EM(void)
8542 {
8543   int i, j, p;
8544
8545 #if DEBUG_EM_GFX
8546   int num_em_gfx_errors = 0;
8547
8548   if (graphic_info_em_object[0][0].bitmap == NULL)
8549   {
8550     // EM graphics not yet initialized in em_open_all()
8551
8552     return;
8553   }
8554
8555   printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8556 #endif
8557
8558   // always start with reliable default values
8559   for (i = 0; i < TILE_MAX; i++)
8560   {
8561     object_mapping[i].element_rnd = EL_UNKNOWN;
8562     object_mapping[i].is_backside = FALSE;
8563     object_mapping[i].action = ACTION_DEFAULT;
8564     object_mapping[i].direction = MV_NONE;
8565   }
8566
8567   // always start with reliable default values
8568   for (p = 0; p < MAX_PLAYERS; p++)
8569   {
8570     for (i = 0; i < PLY_MAX; i++)
8571     {
8572       player_mapping[p][i].element_rnd = EL_UNKNOWN;
8573       player_mapping[p][i].action = ACTION_DEFAULT;
8574       player_mapping[p][i].direction = MV_NONE;
8575     }
8576   }
8577
8578   for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8579   {
8580     int e = em_object_mapping_list[i].element_em;
8581
8582     object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8583     object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8584
8585     if (em_object_mapping_list[i].action != -1)
8586       object_mapping[e].action = em_object_mapping_list[i].action;
8587
8588     if (em_object_mapping_list[i].direction != -1)
8589       object_mapping[e].direction =
8590         MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8591   }
8592
8593   for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8594   {
8595     int a = em_player_mapping_list[i].action_em;
8596     int p = em_player_mapping_list[i].player_nr;
8597
8598     player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8599
8600     if (em_player_mapping_list[i].action != -1)
8601       player_mapping[p][a].action = em_player_mapping_list[i].action;
8602
8603     if (em_player_mapping_list[i].direction != -1)
8604       player_mapping[p][a].direction =
8605         MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8606   }
8607
8608   for (i = 0; i < TILE_MAX; i++)
8609   {
8610     int element = object_mapping[i].element_rnd;
8611     int action = object_mapping[i].action;
8612     int direction = object_mapping[i].direction;
8613     boolean is_backside = object_mapping[i].is_backside;
8614     boolean action_exploding = ((action == ACTION_EXPLODING ||
8615                                  action == ACTION_SMASHED_BY_ROCK ||
8616                                  action == ACTION_SMASHED_BY_SPRING) &&
8617                                 element != EL_DIAMOND);
8618     boolean action_active = (action == ACTION_ACTIVE);
8619     boolean action_other = (action == ACTION_OTHER);
8620
8621     for (j = 0; j < 8; j++)
8622     {
8623       int effective_element = get_effective_element_EM(i, j);
8624       int effective_action = (j < 7 ? action :
8625                               i == Xdrip_stretch ? action :
8626                               i == Xdrip_stretchB ? action :
8627                               i == Ydrip_1_s ? action :
8628                               i == Ydrip_1_sB ? action :
8629                               i == Yball_1 ? action :
8630                               i == Xball_2 ? action :
8631                               i == Yball_2 ? action :
8632                               i == Yball_blank ? action :
8633                               i == Ykey_1_blank ? action :
8634                               i == Ykey_2_blank ? action :
8635                               i == Ykey_3_blank ? action :
8636                               i == Ykey_4_blank ? action :
8637                               i == Ykey_5_blank ? action :
8638                               i == Ykey_6_blank ? action :
8639                               i == Ykey_7_blank ? action :
8640                               i == Ykey_8_blank ? action :
8641                               i == Ylenses_blank ? action :
8642                               i == Ymagnify_blank ? action :
8643                               i == Ygrass_blank ? action :
8644                               i == Ydirt_blank ? action :
8645                               i == Xsand_stonein_1 ? action :
8646                               i == Xsand_stonein_2 ? action :
8647                               i == Xsand_stonein_3 ? action :
8648                               i == Xsand_stonein_4 ? action :
8649                               i == Xsand_stoneout_1 ? action :
8650                               i == Xsand_stoneout_2 ? action :
8651                               i == Xboom_android ? ACTION_EXPLODING :
8652                               action_exploding ? ACTION_EXPLODING :
8653                               action_active ? action :
8654                               action_other ? action :
8655                               ACTION_DEFAULT);
8656       int graphic = (el_act_dir2img(effective_element, effective_action,
8657                                     direction));
8658       int crumbled = (el_act_dir2crm(effective_element, effective_action,
8659                                      direction));
8660       int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8661       int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8662       boolean has_action_graphics = (graphic != base_graphic);
8663       boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8664       struct GraphicInfo *g = &graphic_info[graphic];
8665       struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8666       Bitmap *src_bitmap;
8667       int src_x, src_y;
8668       // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8669       boolean special_animation = (action != ACTION_DEFAULT &&
8670                                    g->anim_frames == 3 &&
8671                                    g->anim_delay == 2 &&
8672                                    g->anim_mode & ANIM_LINEAR);
8673       int sync_frame = (i == Xdrip_stretch ? 7 :
8674                         i == Xdrip_stretchB ? 7 :
8675                         i == Ydrip_2_s ? j + 8 :
8676                         i == Ydrip_2_sB ? j + 8 :
8677                         i == Xacid_1 ? 0 :
8678                         i == Xacid_2 ? 10 :
8679                         i == Xacid_3 ? 20 :
8680                         i == Xacid_4 ? 30 :
8681                         i == Xacid_5 ? 40 :
8682                         i == Xacid_6 ? 50 :
8683                         i == Xacid_7 ? 60 :
8684                         i == Xacid_8 ? 70 :
8685                         i == Xfake_acid_1 ? 0 :
8686                         i == Xfake_acid_2 ? 10 :
8687                         i == Xfake_acid_3 ? 20 :
8688                         i == Xfake_acid_4 ? 30 :
8689                         i == Xfake_acid_5 ? 40 :
8690                         i == Xfake_acid_6 ? 50 :
8691                         i == Xfake_acid_7 ? 60 :
8692                         i == Xfake_acid_8 ? 70 :
8693                         i == Xball_2 ? 7 :
8694                         i == Yball_2 ? j + 8 :
8695                         i == Yball_blank ? j + 1 :
8696                         i == Ykey_1_blank ? j + 1 :
8697                         i == Ykey_2_blank ? j + 1 :
8698                         i == Ykey_3_blank ? j + 1 :
8699                         i == Ykey_4_blank ? j + 1 :
8700                         i == Ykey_5_blank ? j + 1 :
8701                         i == Ykey_6_blank ? j + 1 :
8702                         i == Ykey_7_blank ? j + 1 :
8703                         i == Ykey_8_blank ? j + 1 :
8704                         i == Ylenses_blank ? j + 1 :
8705                         i == Ymagnify_blank ? j + 1 :
8706                         i == Ygrass_blank ? j + 1 :
8707                         i == Ydirt_blank ? j + 1 :
8708                         i == Xamoeba_1 ? 0 :
8709                         i == Xamoeba_2 ? 1 :
8710                         i == Xamoeba_3 ? 2 :
8711                         i == Xamoeba_4 ? 3 :
8712                         i == Xamoeba_5 ? 0 :
8713                         i == Xamoeba_6 ? 1 :
8714                         i == Xamoeba_7 ? 2 :
8715                         i == Xamoeba_8 ? 3 :
8716                         i == Xexit_2 ? j + 8 :
8717                         i == Xexit_3 ? j + 16 :
8718                         i == Xdynamite_1 ? 0 :
8719                         i == Xdynamite_2 ? 8 :
8720                         i == Xdynamite_3 ? 16 :
8721                         i == Xdynamite_4 ? 24 :
8722                         i == Xsand_stonein_1 ? j + 1 :
8723                         i == Xsand_stonein_2 ? j + 9 :
8724                         i == Xsand_stonein_3 ? j + 17 :
8725                         i == Xsand_stonein_4 ? j + 25 :
8726                         i == Xsand_stoneout_1 && j == 0 ? 0 :
8727                         i == Xsand_stoneout_1 && j == 1 ? 0 :
8728                         i == Xsand_stoneout_1 && j == 2 ? 1 :
8729                         i == Xsand_stoneout_1 && j == 3 ? 2 :
8730                         i == Xsand_stoneout_1 && j == 4 ? 2 :
8731                         i == Xsand_stoneout_1 && j == 5 ? 3 :
8732                         i == Xsand_stoneout_1 && j == 6 ? 4 :
8733                         i == Xsand_stoneout_1 && j == 7 ? 4 :
8734                         i == Xsand_stoneout_2 && j == 0 ? 5 :
8735                         i == Xsand_stoneout_2 && j == 1 ? 6 :
8736                         i == Xsand_stoneout_2 && j == 2 ? 7 :
8737                         i == Xsand_stoneout_2 && j == 3 ? 8 :
8738                         i == Xsand_stoneout_2 && j == 4 ? 9 :
8739                         i == Xsand_stoneout_2 && j == 5 ? 11 :
8740                         i == Xsand_stoneout_2 && j == 6 ? 13 :
8741                         i == Xsand_stoneout_2 && j == 7 ? 15 :
8742                         i == Xboom_bug && j == 1 ? 2 :
8743                         i == Xboom_bug && j == 2 ? 2 :
8744                         i == Xboom_bug && j == 3 ? 4 :
8745                         i == Xboom_bug && j == 4 ? 4 :
8746                         i == Xboom_bug && j == 5 ? 2 :
8747                         i == Xboom_bug && j == 6 ? 2 :
8748                         i == Xboom_bug && j == 7 ? 0 :
8749                         i == Xboom_bomb && j == 1 ? 2 :
8750                         i == Xboom_bomb && j == 2 ? 2 :
8751                         i == Xboom_bomb && j == 3 ? 4 :
8752                         i == Xboom_bomb && j == 4 ? 4 :
8753                         i == Xboom_bomb && j == 5 ? 2 :
8754                         i == Xboom_bomb && j == 6 ? 2 :
8755                         i == Xboom_bomb && j == 7 ? 0 :
8756                         i == Xboom_android && j == 7 ? 6 :
8757                         i == Xboom_1 && j == 1 ? 2 :
8758                         i == Xboom_1 && j == 2 ? 2 :
8759                         i == Xboom_1 && j == 3 ? 4 :
8760                         i == Xboom_1 && j == 4 ? 4 :
8761                         i == Xboom_1 && j == 5 ? 6 :
8762                         i == Xboom_1 && j == 6 ? 6 :
8763                         i == Xboom_1 && j == 7 ? 8 :
8764                         i == Xboom_2 && j == 0 ? 8 :
8765                         i == Xboom_2 && j == 1 ? 8 :
8766                         i == Xboom_2 && j == 2 ? 10 :
8767                         i == Xboom_2 && j == 3 ? 10 :
8768                         i == Xboom_2 && j == 4 ? 10 :
8769                         i == Xboom_2 && j == 5 ? 12 :
8770                         i == Xboom_2 && j == 6 ? 12 :
8771                         i == Xboom_2 && j == 7 ? 12 :
8772                         special_animation && j == 4 ? 3 :
8773                         effective_action != action ? 0 :
8774                         j);
8775
8776 #if DEBUG_EM_GFX
8777       Bitmap *debug_bitmap = g_em->bitmap;
8778       int debug_src_x = g_em->src_x;
8779       int debug_src_y = g_em->src_y;
8780 #endif
8781
8782       int frame = getAnimationFrame(g->anim_frames,
8783                                     g->anim_delay,
8784                                     g->anim_mode,
8785                                     g->anim_start_frame,
8786                                     sync_frame);
8787
8788       getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8789                           g->double_movement && is_backside);
8790
8791       g_em->bitmap = src_bitmap;
8792       g_em->src_x = src_x;
8793       g_em->src_y = src_y;
8794       g_em->src_offset_x = 0;
8795       g_em->src_offset_y = 0;
8796       g_em->dst_offset_x = 0;
8797       g_em->dst_offset_y = 0;
8798       g_em->width  = TILEX;
8799       g_em->height = TILEY;
8800
8801       g_em->preserve_background = FALSE;
8802
8803       set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8804                                sync_frame);
8805
8806       if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8807                                    effective_action == ACTION_MOVING  ||
8808                                    effective_action == ACTION_PUSHING ||
8809                                    effective_action == ACTION_EATING)) ||
8810           (!has_action_graphics && (effective_action == ACTION_FILLING ||
8811                                     effective_action == ACTION_EMPTYING)))
8812       {
8813         int move_dir =
8814           (effective_action == ACTION_FALLING ||
8815            effective_action == ACTION_FILLING ||
8816            effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8817         int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8818         int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? 1 : 0);
8819         int num_steps = (i == Ydrip_1_s  ? 16 :
8820                          i == Ydrip_1_sB ? 16 :
8821                          i == Ydrip_2_s  ? 16 :
8822                          i == Ydrip_2_sB ? 16 :
8823                          i == Xsand_stonein_1 ? 32 :
8824                          i == Xsand_stonein_2 ? 32 :
8825                          i == Xsand_stonein_3 ? 32 :
8826                          i == Xsand_stonein_4 ? 32 :
8827                          i == Xsand_stoneout_1 ? 16 :
8828                          i == Xsand_stoneout_2 ? 16 : 8);
8829         int cx = ABS(dx) * (TILEX / num_steps);
8830         int cy = ABS(dy) * (TILEY / num_steps);
8831         int step_frame = (i == Ydrip_2_s        ? j + 8 :
8832                           i == Ydrip_2_sB       ? j + 8 :
8833                           i == Xsand_stonein_2  ? j + 8 :
8834                           i == Xsand_stonein_3  ? j + 16 :
8835                           i == Xsand_stonein_4  ? j + 24 :
8836                           i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8837         int step = (is_backside ? step_frame : num_steps - step_frame);
8838
8839         if (is_backside)        // tile where movement starts
8840         {
8841           if (dx < 0 || dy < 0)
8842           {
8843             g_em->src_offset_x = cx * step;
8844             g_em->src_offset_y = cy * step;
8845           }
8846           else
8847           {
8848             g_em->dst_offset_x = cx * step;
8849             g_em->dst_offset_y = cy * step;
8850           }
8851         }
8852         else                    // tile where movement ends
8853         {
8854           if (dx < 0 || dy < 0)
8855           {
8856             g_em->dst_offset_x = cx * step;
8857             g_em->dst_offset_y = cy * step;
8858           }
8859           else
8860           {
8861             g_em->src_offset_x = cx * step;
8862             g_em->src_offset_y = cy * step;
8863           }
8864         }
8865
8866         g_em->width  = TILEX - cx * step;
8867         g_em->height = TILEY - cy * step;
8868       }
8869
8870       // create unique graphic identifier to decide if tile must be redrawn
8871       /* bit 31 - 16 (16 bit): EM style graphic
8872          bit 15 - 12 ( 4 bit): EM style frame
8873          bit 11 -  6 ( 6 bit): graphic width
8874          bit  5 -  0 ( 6 bit): graphic height */
8875       g_em->unique_identifier =
8876         (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8877
8878 #if DEBUG_EM_GFX
8879
8880       // skip check for EMC elements not contained in original EMC artwork
8881       if (element == EL_EMC_FAKE_ACID)
8882         continue;
8883
8884       if (g_em->bitmap != debug_bitmap ||
8885           g_em->src_x != debug_src_x ||
8886           g_em->src_y != debug_src_y ||
8887           g_em->src_offset_x != 0 ||
8888           g_em->src_offset_y != 0 ||
8889           g_em->dst_offset_x != 0 ||
8890           g_em->dst_offset_y != 0 ||
8891           g_em->width != TILEX ||
8892           g_em->height != TILEY)
8893       {
8894         static int last_i = -1;
8895
8896         if (i != last_i)
8897         {
8898           printf("\n");
8899           last_i = i;
8900         }
8901
8902         printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8903                i, element, element_info[element].token_name,
8904                element_action_info[effective_action].suffix, direction);
8905
8906         if (element != effective_element)
8907           printf(" [%d ('%s')]",
8908                  effective_element,
8909                  element_info[effective_element].token_name);
8910
8911         printf("\n");
8912
8913         if (g_em->bitmap != debug_bitmap)
8914           printf("    %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8915                  j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8916
8917         if (g_em->src_x != debug_src_x ||
8918             g_em->src_y != debug_src_y)
8919           printf("    frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8920                  j, (is_backside ? 'B' : 'F'),
8921                  g_em->src_x, g_em->src_y,
8922                  g_em->src_x / 32, g_em->src_y / 32,
8923                  debug_src_x, debug_src_y,
8924                  debug_src_x / 32, debug_src_y / 32);
8925
8926         if (g_em->src_offset_x != 0 ||
8927             g_em->src_offset_y != 0 ||
8928             g_em->dst_offset_x != 0 ||
8929             g_em->dst_offset_y != 0)
8930           printf("    %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8931                  j, is_backside,
8932                  g_em->src_offset_x, g_em->src_offset_y,
8933                  g_em->dst_offset_x, g_em->dst_offset_y);
8934
8935         if (g_em->width != TILEX ||
8936             g_em->height != TILEY)
8937           printf("    %d (%d): size %d,%d should be %d,%d\n",
8938                  j, is_backside,
8939                  g_em->width, g_em->height, TILEX, TILEY);
8940
8941         num_em_gfx_errors++;
8942       }
8943 #endif
8944
8945     }
8946   }
8947
8948   for (i = 0; i < TILE_MAX; i++)
8949   {
8950     for (j = 0; j < 8; j++)
8951     {
8952       int element = object_mapping[i].element_rnd;
8953       int action = object_mapping[i].action;
8954       int direction = object_mapping[i].direction;
8955       boolean is_backside = object_mapping[i].is_backside;
8956       int graphic_action  = el_act_dir2img(element, action, direction);
8957       int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8958
8959       if ((action == ACTION_SMASHED_BY_ROCK ||
8960            action == ACTION_SMASHED_BY_SPRING ||
8961            action == ACTION_EATING) &&
8962           graphic_action == graphic_default)
8963       {
8964         int e = (action == ACTION_SMASHED_BY_ROCK   ? Ystone_s  :
8965                  action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8966                  direction == MV_LEFT  ? (is_backside? Yspring_wB: Yspring_w) :
8967                  direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8968                  Xspring);
8969
8970         // no separate animation for "smashed by rock" -- use rock instead
8971         struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8972         struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8973
8974         g_em->bitmap            = g_xx->bitmap;
8975         g_em->src_x             = g_xx->src_x;
8976         g_em->src_y             = g_xx->src_y;
8977         g_em->src_offset_x      = g_xx->src_offset_x;
8978         g_em->src_offset_y      = g_xx->src_offset_y;
8979         g_em->dst_offset_x      = g_xx->dst_offset_x;
8980         g_em->dst_offset_y      = g_xx->dst_offset_y;
8981         g_em->width             = g_xx->width;
8982         g_em->height            = g_xx->height;
8983         g_em->unique_identifier = g_xx->unique_identifier;
8984
8985         if (!is_backside)
8986           g_em->preserve_background = TRUE;
8987       }
8988     }
8989   }
8990
8991   for (p = 0; p < MAX_PLAYERS; p++)
8992   {
8993     for (i = 0; i < PLY_MAX; i++)
8994     {
8995       int element = player_mapping[p][i].element_rnd;
8996       int action = player_mapping[p][i].action;
8997       int direction = player_mapping[p][i].direction;
8998
8999       for (j = 0; j < 8; j++)
9000       {
9001         int effective_element = element;
9002         int effective_action = action;
9003         int graphic = (direction == MV_NONE ?
9004                        el_act2img(effective_element, effective_action) :
9005                        el_act_dir2img(effective_element, effective_action,
9006                                       direction));
9007         struct GraphicInfo *g = &graphic_info[graphic];
9008         struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
9009         Bitmap *src_bitmap;
9010         int src_x, src_y;
9011         int sync_frame = j;
9012
9013 #if DEBUG_EM_GFX
9014         Bitmap *debug_bitmap = g_em->bitmap;
9015         int debug_src_x = g_em->src_x;
9016         int debug_src_y = g_em->src_y;
9017 #endif
9018
9019         int frame = getAnimationFrame(g->anim_frames,
9020                                       g->anim_delay,
9021                                       g->anim_mode,
9022                                       g->anim_start_frame,
9023                                       sync_frame);
9024
9025         getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9026
9027         g_em->bitmap = src_bitmap;
9028         g_em->src_x = src_x;
9029         g_em->src_y = src_y;
9030         g_em->src_offset_x = 0;
9031         g_em->src_offset_y = 0;
9032         g_em->dst_offset_x = 0;
9033         g_em->dst_offset_y = 0;
9034         g_em->width  = TILEX;
9035         g_em->height = TILEY;
9036
9037 #if DEBUG_EM_GFX
9038
9039         // skip check for EMC elements not contained in original EMC artwork
9040         if (element == EL_PLAYER_3 ||
9041             element == EL_PLAYER_4)
9042           continue;
9043
9044         if (g_em->bitmap != debug_bitmap ||
9045             g_em->src_x != debug_src_x ||
9046             g_em->src_y != debug_src_y)
9047         {
9048           static int last_i = -1;
9049
9050           if (i != last_i)
9051           {
9052             printf("\n");
9053             last_i = i;
9054           }
9055
9056           printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
9057                  p, i, element, element_info[element].token_name,
9058                  element_action_info[effective_action].suffix, direction);
9059
9060           if (element != effective_element)
9061             printf(" [%d ('%s')]",
9062                    effective_element,
9063                    element_info[effective_element].token_name);
9064
9065           printf("\n");
9066
9067           if (g_em->bitmap != debug_bitmap)
9068             printf("    %d: different bitmap! (0x%08x != 0x%08x)\n",
9069                    j, (int)(g_em->bitmap), (int)(debug_bitmap));
9070
9071           if (g_em->src_x != debug_src_x ||
9072               g_em->src_y != debug_src_y)
9073             printf("    frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
9074                    j,
9075                    g_em->src_x, g_em->src_y,
9076                    g_em->src_x / 32, g_em->src_y / 32,
9077                    debug_src_x, debug_src_y,
9078                    debug_src_x / 32, debug_src_y / 32);
9079
9080           num_em_gfx_errors++;
9081         }
9082 #endif
9083
9084       }
9085     }
9086   }
9087
9088 #if DEBUG_EM_GFX
9089   printf("\n");
9090   printf("::: [%d errors found]\n", num_em_gfx_errors);
9091
9092   exit(0);
9093 #endif
9094 }
9095
9096 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9097                                        boolean any_player_moving,
9098                                        boolean any_player_snapping,
9099                                        boolean any_player_dropping)
9100 {
9101   if (frame == 0 && !any_player_dropping)
9102   {
9103     if (!local_player->was_waiting)
9104     {
9105       if (!CheckSaveEngineSnapshotToList())
9106         return;
9107
9108       local_player->was_waiting = TRUE;
9109     }
9110   }
9111   else if (any_player_moving || any_player_snapping || any_player_dropping)
9112   {
9113     local_player->was_waiting = FALSE;
9114   }
9115 }
9116
9117 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9118                                        boolean murphy_is_dropping)
9119 {
9120   if (murphy_is_waiting)
9121   {
9122     if (!local_player->was_waiting)
9123     {
9124       if (!CheckSaveEngineSnapshotToList())
9125         return;
9126
9127       local_player->was_waiting = TRUE;
9128     }
9129   }
9130   else
9131   {
9132     local_player->was_waiting = FALSE;
9133   }
9134 }
9135
9136 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9137                                        boolean button_released)
9138 {
9139   if (button_released)
9140   {
9141     if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9142       CheckSaveEngineSnapshotToList();
9143   }
9144   else if (element_clicked)
9145   {
9146     if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9147       CheckSaveEngineSnapshotToList();
9148
9149     game.snapshot.changed_action = TRUE;
9150   }
9151 }
9152
9153 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9154                             boolean any_player_moving,
9155                             boolean any_player_snapping,
9156                             boolean any_player_dropping)
9157 {
9158   if (tape.single_step && tape.recording && !tape.pausing)
9159     if (frame == 0 && !any_player_dropping)
9160       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9161
9162   CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9163                              any_player_snapping, any_player_dropping);
9164 }
9165
9166 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9167                             boolean murphy_is_dropping)
9168 {
9169   boolean murphy_starts_dropping = FALSE;
9170   int i;
9171
9172   for (i = 0; i < MAX_PLAYERS; i++)
9173     if (stored_player[i].force_dropping)
9174       murphy_starts_dropping = TRUE;
9175
9176   if (tape.single_step && tape.recording && !tape.pausing)
9177     if (murphy_is_waiting && !murphy_starts_dropping)
9178       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9179
9180   CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9181 }
9182
9183 void CheckSingleStepMode_MM(boolean element_clicked,
9184                             boolean button_released)
9185 {
9186   if (tape.single_step && tape.recording && !tape.pausing)
9187     if (button_released)
9188       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9189
9190   CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9191 }
9192
9193 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9194                          int graphic, int sync_frame, int x, int y)
9195 {
9196   int frame = getGraphicAnimationFrame(graphic, sync_frame);
9197
9198   getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9199 }
9200
9201 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9202 {
9203   return (IS_NEXT_FRAME(sync_frame, graphic));
9204 }
9205
9206 int getGraphicInfo_Delay(int graphic)
9207 {
9208   return graphic_info[graphic].anim_delay;
9209 }
9210
9211 void PlayMenuSoundExt(int sound)
9212 {
9213   if (sound == SND_UNDEFINED)
9214     return;
9215
9216   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9217       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9218     return;
9219
9220   if (IS_LOOP_SOUND(sound))
9221     PlaySoundLoop(sound);
9222   else
9223     PlaySound(sound);
9224 }
9225
9226 void PlayMenuSound(void)
9227 {
9228   PlayMenuSoundExt(menu.sound[game_status]);
9229 }
9230
9231 void PlayMenuSoundStereo(int sound, int stereo_position)
9232 {
9233   if (sound == SND_UNDEFINED)
9234     return;
9235
9236   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9237       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9238     return;
9239
9240   if (IS_LOOP_SOUND(sound))
9241     PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9242   else
9243     PlaySoundStereo(sound, stereo_position);
9244 }
9245
9246 void PlayMenuSoundIfLoopExt(int sound)
9247 {
9248   if (sound == SND_UNDEFINED)
9249     return;
9250
9251   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9252       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9253     return;
9254
9255   if (IS_LOOP_SOUND(sound))
9256     PlaySoundLoop(sound);
9257 }
9258
9259 void PlayMenuSoundIfLoop(void)
9260 {
9261   PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9262 }
9263
9264 void PlayMenuMusicExt(int music)
9265 {
9266   if (music == MUS_UNDEFINED)
9267     return;
9268
9269   if (!setup.sound_music)
9270     return;
9271
9272   if (IS_LOOP_MUSIC(music))
9273     PlayMusicLoop(music);
9274   else
9275     PlayMusic(music);
9276 }
9277
9278 void PlayMenuMusic(void)
9279 {
9280   char *curr_music = getCurrentlyPlayingMusicFilename();
9281   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9282
9283   if (!strEqual(curr_music, next_music))
9284     PlayMenuMusicExt(menu.music[game_status]);
9285 }
9286
9287 void PlayMenuSoundsAndMusic(void)
9288 {
9289   PlayMenuSound();
9290   PlayMenuMusic();
9291 }
9292
9293 static void FadeMenuSounds(void)
9294 {
9295   FadeSounds();
9296 }
9297
9298 static void FadeMenuMusic(void)
9299 {
9300   char *curr_music = getCurrentlyPlayingMusicFilename();
9301   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9302
9303   if (!strEqual(curr_music, next_music))
9304     FadeMusic();
9305 }
9306
9307 void FadeMenuSoundsAndMusic(void)
9308 {
9309   FadeMenuSounds();
9310   FadeMenuMusic();
9311 }
9312
9313 void PlaySoundActivating(void)
9314 {
9315 #if 0
9316   PlaySound(SND_MENU_ITEM_ACTIVATING);
9317 #endif
9318 }
9319
9320 void PlaySoundSelecting(void)
9321 {
9322 #if 0
9323   PlaySound(SND_MENU_ITEM_SELECTING);
9324 #endif
9325 }
9326
9327 void ToggleFullscreenOrChangeWindowScalingIfNeeded(void)
9328 {
9329   boolean change_fullscreen = (setup.fullscreen !=
9330                                video.fullscreen_enabled);
9331   boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9332                                            setup.window_scaling_percent !=
9333                                            video.window_scaling_percent);
9334
9335   if (change_window_scaling_percent && video.fullscreen_enabled)
9336     return;
9337
9338   if (!change_window_scaling_percent && !video.fullscreen_available)
9339     return;
9340
9341   if (change_window_scaling_percent)
9342   {
9343     SDLSetWindowScaling(setup.window_scaling_percent);
9344
9345     return;
9346   }
9347   else if (change_fullscreen)
9348   {
9349     SDLSetWindowFullscreen(setup.fullscreen);
9350
9351     // set setup value according to successfully changed fullscreen mode
9352     setup.fullscreen = video.fullscreen_enabled;
9353
9354     return;
9355   }
9356
9357   if (change_fullscreen ||
9358       change_window_scaling_percent)
9359   {
9360     Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9361
9362     // save backbuffer content which gets lost when toggling fullscreen mode
9363     BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9364
9365     if (change_window_scaling_percent)
9366     {
9367       // keep window mode, but change window scaling
9368       video.fullscreen_enabled = TRUE;          // force new window scaling
9369     }
9370
9371     // toggle fullscreen
9372     ChangeVideoModeIfNeeded(setup.fullscreen);
9373
9374     // set setup value according to successfully changed fullscreen mode
9375     setup.fullscreen = video.fullscreen_enabled;
9376
9377     // restore backbuffer content from temporary backbuffer backup bitmap
9378     BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9379
9380     FreeBitmap(tmp_backbuffer);
9381
9382     // update visible window/screen
9383     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9384   }
9385 }
9386
9387 static void JoinRectangles(int *x, int *y, int *width, int *height,
9388                            int x2, int y2, int width2, int height2)
9389 {
9390   // do not join with "off-screen" rectangle
9391   if (x2 == -1 || y2 == -1)
9392     return;
9393
9394   *x = MIN(*x, x2);
9395   *y = MIN(*y, y2);
9396   *width = MAX(*width, width2);
9397   *height = MAX(*height, height2);
9398 }
9399
9400 void SetAnimStatus(int anim_status_new)
9401 {
9402   if (anim_status_new == GAME_MODE_MAIN)
9403     anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9404   else if (anim_status_new == GAME_MODE_SCORES)
9405     anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9406
9407   global.anim_status_next = anim_status_new;
9408
9409   // directly set screen modes that are entered without fading
9410   if ((global.anim_status      == GAME_MODE_PSEUDO_MAINONLY &&
9411        global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9412       (global.anim_status      == GAME_MODE_PSEUDO_TYPENAME &&
9413        global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9414     global.anim_status = global.anim_status_next;
9415 }
9416
9417 void SetGameStatus(int game_status_new)
9418 {
9419   if (game_status_new != game_status)
9420     game_status_last_screen = game_status;
9421
9422   game_status = game_status_new;
9423
9424   SetAnimStatus(game_status_new);
9425 }
9426
9427 void SetFontStatus(int game_status_new)
9428 {
9429   static int last_game_status = -1;
9430
9431   if (game_status_new != -1)
9432   {
9433     // set game status for font use after storing last game status
9434     last_game_status = game_status;
9435     game_status = game_status_new;
9436   }
9437   else
9438   {
9439     // reset game status after font use from last stored game status
9440     game_status = last_game_status;
9441   }
9442 }
9443
9444 void ResetFontStatus(void)
9445 {
9446   SetFontStatus(-1);
9447 }
9448
9449 void SetLevelSetInfo(char *identifier, int level_nr)
9450 {
9451   setString(&levelset.identifier, identifier);
9452
9453   levelset.level_nr = level_nr;
9454 }
9455
9456 boolean CheckIfAllViewportsHaveChanged(void)
9457 {
9458   // if game status has not changed, viewports have not changed either
9459   if (game_status == game_status_last)
9460     return FALSE;
9461
9462   // check if all viewports have changed with current game status
9463
9464   struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9465   struct RectWithBorder *vp_door_1    = &viewport.door_1[game_status];
9466   struct RectWithBorder *vp_door_2    = &viewport.door_2[game_status];
9467   int new_real_sx       = vp_playfield->x;
9468   int new_real_sy       = vp_playfield->y;
9469   int new_full_sxsize   = vp_playfield->width;
9470   int new_full_sysize   = vp_playfield->height;
9471   int new_dx            = vp_door_1->x;
9472   int new_dy            = vp_door_1->y;
9473   int new_dxsize        = vp_door_1->width;
9474   int new_dysize        = vp_door_1->height;
9475   int new_vx            = vp_door_2->x;
9476   int new_vy            = vp_door_2->y;
9477   int new_vxsize        = vp_door_2->width;
9478   int new_vysize        = vp_door_2->height;
9479
9480   boolean playfield_viewport_has_changed =
9481     (new_real_sx != REAL_SX ||
9482      new_real_sy != REAL_SY ||
9483      new_full_sxsize != FULL_SXSIZE ||
9484      new_full_sysize != FULL_SYSIZE);
9485
9486   boolean door_1_viewport_has_changed =
9487     (new_dx != DX ||
9488      new_dy != DY ||
9489      new_dxsize != DXSIZE ||
9490      new_dysize != DYSIZE);
9491
9492   boolean door_2_viewport_has_changed =
9493     (new_vx != VX ||
9494      new_vy != VY ||
9495      new_vxsize != VXSIZE ||
9496      new_vysize != VYSIZE ||
9497      game_status_last == GAME_MODE_EDITOR);
9498
9499   return (playfield_viewport_has_changed &&
9500           door_1_viewport_has_changed &&
9501           door_2_viewport_has_changed);
9502 }
9503
9504 boolean CheckFadeAll(void)
9505 {
9506   return (CheckIfGlobalBorderHasChanged() ||
9507           CheckIfAllViewportsHaveChanged());
9508 }
9509
9510 void ChangeViewportPropertiesIfNeeded(void)
9511 {
9512   boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9513                                FALSE : setup.small_game_graphics);
9514   int gfx_game_mode = game_status;
9515   int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9516                         game_status);
9517   struct RectWithBorder *vp_window    = &viewport.window[gfx_game_mode];
9518   struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9519   struct RectWithBorder *vp_door_1    = &viewport.door_1[gfx_game_mode];
9520   struct RectWithBorder *vp_door_2    = &viewport.door_2[gfx_game_mode2];
9521   struct RectWithBorder *vp_door_3    = &viewport.door_2[GAME_MODE_EDITOR];
9522   int new_win_xsize     = vp_window->width;
9523   int new_win_ysize     = vp_window->height;
9524   int border_left       = vp_playfield->border_left;
9525   int border_right      = vp_playfield->border_right;
9526   int border_top        = vp_playfield->border_top;
9527   int border_bottom     = vp_playfield->border_bottom;
9528   int new_sx            = vp_playfield->x      + border_left;
9529   int new_sy            = vp_playfield->y      + border_top;
9530   int new_sxsize        = vp_playfield->width  - border_left - border_right;
9531   int new_sysize        = vp_playfield->height - border_top  - border_bottom;
9532   int new_real_sx       = vp_playfield->x;
9533   int new_real_sy       = vp_playfield->y;
9534   int new_full_sxsize   = vp_playfield->width;
9535   int new_full_sysize   = vp_playfield->height;
9536   int new_dx            = vp_door_1->x;
9537   int new_dy            = vp_door_1->y;
9538   int new_dxsize        = vp_door_1->width;
9539   int new_dysize        = vp_door_1->height;
9540   int new_vx            = vp_door_2->x;
9541   int new_vy            = vp_door_2->y;
9542   int new_vxsize        = vp_door_2->width;
9543   int new_vysize        = vp_door_2->height;
9544   int new_ex            = vp_door_3->x;
9545   int new_ey            = vp_door_3->y;
9546   int new_exsize        = vp_door_3->width;
9547   int new_eysize        = vp_door_3->height;
9548   int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9549   int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9550                   gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9551   int new_scr_fieldx = new_sxsize / tilesize;
9552   int new_scr_fieldy = new_sysize / tilesize;
9553   int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9554   int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9555   boolean init_gfx_buffers = FALSE;
9556   boolean init_video_buffer = FALSE;
9557   boolean init_gadgets_and_anims = FALSE;
9558   boolean init_em_graphics = FALSE;
9559
9560   if (new_win_xsize != WIN_XSIZE ||
9561       new_win_ysize != WIN_YSIZE)
9562   {
9563     WIN_XSIZE = new_win_xsize;
9564     WIN_YSIZE = new_win_ysize;
9565
9566     init_video_buffer = TRUE;
9567     init_gfx_buffers = TRUE;
9568     init_gadgets_and_anims = TRUE;
9569
9570     // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9571   }
9572
9573   if (new_scr_fieldx != SCR_FIELDX ||
9574       new_scr_fieldy != SCR_FIELDY)
9575   {
9576     // this always toggles between MAIN and GAME when using small tile size
9577
9578     SCR_FIELDX = new_scr_fieldx;
9579     SCR_FIELDY = new_scr_fieldy;
9580
9581     // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9582   }
9583
9584   if (new_sx != SX ||
9585       new_sy != SY ||
9586       new_dx != DX ||
9587       new_dy != DY ||
9588       new_vx != VX ||
9589       new_vy != VY ||
9590       new_ex != EX ||
9591       new_ey != EY ||
9592       new_sxsize != SXSIZE ||
9593       new_sysize != SYSIZE ||
9594       new_dxsize != DXSIZE ||
9595       new_dysize != DYSIZE ||
9596       new_vxsize != VXSIZE ||
9597       new_vysize != VYSIZE ||
9598       new_exsize != EXSIZE ||
9599       new_eysize != EYSIZE ||
9600       new_real_sx != REAL_SX ||
9601       new_real_sy != REAL_SY ||
9602       new_full_sxsize != FULL_SXSIZE ||
9603       new_full_sysize != FULL_SYSIZE ||
9604       new_tilesize_var != TILESIZE_VAR
9605       )
9606   {
9607     // ------------------------------------------------------------------------
9608     // determine next fading area for changed viewport definitions
9609     // ------------------------------------------------------------------------
9610
9611     // start with current playfield area (default fading area)
9612     FADE_SX = REAL_SX;
9613     FADE_SY = REAL_SY;
9614     FADE_SXSIZE = FULL_SXSIZE;
9615     FADE_SYSIZE = FULL_SYSIZE;
9616
9617     // add new playfield area if position or size has changed
9618     if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9619         new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9620     {
9621       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9622                      new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9623     }
9624
9625     // add current and new door 1 area if position or size has changed
9626     if (new_dx != DX || new_dy != DY ||
9627         new_dxsize != DXSIZE || new_dysize != DYSIZE)
9628     {
9629       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9630                      DX, DY, DXSIZE, DYSIZE);
9631       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9632                      new_dx, new_dy, new_dxsize, new_dysize);
9633     }
9634
9635     // add current and new door 2 area if position or size has changed
9636     if (new_vx != VX || new_vy != VY ||
9637         new_vxsize != VXSIZE || new_vysize != VYSIZE)
9638     {
9639       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9640                      VX, VY, VXSIZE, VYSIZE);
9641       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9642                      new_vx, new_vy, new_vxsize, new_vysize);
9643     }
9644
9645     // ------------------------------------------------------------------------
9646     // handle changed tile size
9647     // ------------------------------------------------------------------------
9648
9649     if (new_tilesize_var != TILESIZE_VAR)
9650     {
9651       // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9652
9653       // changing tile size invalidates scroll values of engine snapshots
9654       FreeEngineSnapshotSingle();
9655
9656       // changing tile size requires update of graphic mapping for EM engine
9657       init_em_graphics = TRUE;
9658     }
9659
9660     SX = new_sx;
9661     SY = new_sy;
9662     DX = new_dx;
9663     DY = new_dy;
9664     VX = new_vx;
9665     VY = new_vy;
9666     EX = new_ex;
9667     EY = new_ey;
9668     SXSIZE = new_sxsize;
9669     SYSIZE = new_sysize;
9670     DXSIZE = new_dxsize;
9671     DYSIZE = new_dysize;
9672     VXSIZE = new_vxsize;
9673     VYSIZE = new_vysize;
9674     EXSIZE = new_exsize;
9675     EYSIZE = new_eysize;
9676     REAL_SX = new_real_sx;
9677     REAL_SY = new_real_sy;
9678     FULL_SXSIZE = new_full_sxsize;
9679     FULL_SYSIZE = new_full_sysize;
9680     TILESIZE_VAR = new_tilesize_var;
9681
9682     init_gfx_buffers = TRUE;
9683     init_gadgets_and_anims = TRUE;
9684
9685     // printf("::: viewports: init_gfx_buffers\n");
9686     // printf("::: viewports: init_gadgets_and_anims\n");
9687   }
9688
9689   if (init_gfx_buffers)
9690   {
9691     // printf("::: init_gfx_buffers\n");
9692
9693     SCR_FIELDX = new_scr_fieldx_buffers;
9694     SCR_FIELDY = new_scr_fieldy_buffers;
9695
9696     InitGfxBuffers();
9697
9698     SCR_FIELDX = new_scr_fieldx;
9699     SCR_FIELDY = new_scr_fieldy;
9700
9701     SetDrawDeactivationMask(REDRAW_NONE);
9702     SetDrawBackgroundMask(REDRAW_FIELD);
9703   }
9704
9705   if (init_video_buffer)
9706   {
9707     // printf("::: init_video_buffer\n");
9708
9709     FreeAllImageTextures();     // needs old renderer to free the textures
9710
9711     InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9712     InitImageTextures();
9713   }
9714
9715   if (init_gadgets_and_anims)
9716   {
9717     // printf("::: init_gadgets_and_anims\n");
9718
9719     InitGadgets();
9720     InitGlobalAnimations();
9721   }
9722
9723   if (init_em_graphics)
9724   {
9725       InitGraphicInfo_EM();
9726   }
9727 }