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