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