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