swapped temporary bitmaps used for request dialog (no functional change)
[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_or_moving &&
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   game.request_active = TRUE;
4512
4513   setRequestPosition(&sx, &sy, FALSE);
4514
4515   button_status = MB_RELEASED;
4516
4517   request_gadget_id = -1;
4518   result = -1;
4519
4520   while (result < 0)
4521   {
4522     if (game_just_ended)
4523     {
4524       SetDrawtoField(draw_buffer_game);
4525
4526       HandleGameActions();
4527
4528       SetDrawtoField(DRAW_TO_BACKBUFFER);
4529     }
4530
4531     if (PendingEvent())
4532     {
4533       Event event;
4534
4535       while (NextValidEvent(&event))
4536       {
4537         switch (event.type)
4538         {
4539           case EVENT_BUTTONPRESS:
4540           case EVENT_BUTTONRELEASE:
4541           case EVENT_MOTIONNOTIFY:
4542           {
4543             DrawBuffer *drawto_last = drawto;
4544             int mx, my;
4545
4546             if (event.type == EVENT_MOTIONNOTIFY)
4547             {
4548               if (!button_status)
4549                 continue;
4550
4551               motion_status = TRUE;
4552               mx = ((MotionEvent *) &event)->x;
4553               my = ((MotionEvent *) &event)->y;
4554             }
4555             else
4556             {
4557               motion_status = FALSE;
4558               mx = ((ButtonEvent *) &event)->x;
4559               my = ((ButtonEvent *) &event)->y;
4560               if (event.type == EVENT_BUTTONPRESS)
4561                 button_status = ((ButtonEvent *) &event)->button;
4562               else
4563                 button_status = MB_RELEASED;
4564             }
4565
4566             if (global.use_envelope_request)
4567             {
4568               // draw changed button states to temporary bitmap
4569               drawto = bitmap_db_store_1;
4570             }
4571
4572             // this sets 'request_gadget_id'
4573             HandleGadgets(mx, my, button_status);
4574
4575             if (global.use_envelope_request)
4576             {
4577               // restore pointer to drawing buffer
4578               drawto = drawto_last;
4579
4580               // prepare complete envelope request from temporary bitmap
4581               PrepareEnvelopeRequestToScreen(bitmap_db_store_1, sx, sy,
4582                                              width, height);
4583             }
4584
4585             switch (request_gadget_id)
4586             {
4587               case TOOL_CTRL_ID_YES:
4588               case TOOL_CTRL_ID_TOUCH_YES:
4589                 result = TRUE;
4590                 break;
4591               case TOOL_CTRL_ID_NO:
4592               case TOOL_CTRL_ID_TOUCH_NO:
4593                 result = FALSE;
4594                 break;
4595               case TOOL_CTRL_ID_CONFIRM:
4596               case TOOL_CTRL_ID_TOUCH_CONFIRM:
4597                 result = TRUE | FALSE;
4598                 break;
4599
4600               case TOOL_CTRL_ID_PLAYER_1:
4601                 result = 1;
4602                 break;
4603               case TOOL_CTRL_ID_PLAYER_2:
4604                 result = 2;
4605                 break;
4606               case TOOL_CTRL_ID_PLAYER_3:
4607                 result = 3;
4608                 break;
4609               case TOOL_CTRL_ID_PLAYER_4:
4610                 result = 4;
4611                 break;
4612
4613               default:
4614                 // only check clickable animations if no request gadget clicked
4615                 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4616                 break;
4617             }
4618
4619             break;
4620           }
4621
4622           case SDL_WINDOWEVENT:
4623             HandleWindowEvent((WindowEvent *) &event);
4624             break;
4625
4626           case SDL_APP_WILLENTERBACKGROUND:
4627           case SDL_APP_DIDENTERBACKGROUND:
4628           case SDL_APP_WILLENTERFOREGROUND:
4629           case SDL_APP_DIDENTERFOREGROUND:
4630             HandlePauseResumeEvent((PauseResumeEvent *) &event);
4631             break;
4632
4633           case EVENT_KEYPRESS:
4634           {
4635             Key key = GetEventKey((KeyEvent *)&event);
4636
4637             switch (key)
4638             {
4639               case KSYM_space:
4640                 if (req_state & REQ_CONFIRM)
4641                   result = 1;
4642                 break;
4643
4644               case KSYM_Return:
4645               case KSYM_y:
4646               case KSYM_Y:
4647               case KSYM_Select:
4648               case KSYM_Menu:
4649 #if defined(KSYM_Rewind)
4650               case KSYM_Rewind:         // for Amazon Fire TV remote
4651 #endif
4652                 result = 1;
4653                 break;
4654
4655               case KSYM_Escape:
4656               case KSYM_n:
4657               case KSYM_N:
4658               case KSYM_Back:
4659 #if defined(KSYM_FastForward)
4660               case KSYM_FastForward:    // for Amazon Fire TV remote
4661 #endif
4662                 result = 0;
4663                 break;
4664
4665               default:
4666                 HandleKeysDebug(key, KEY_PRESSED);
4667                 break;
4668             }
4669
4670             if (req_state & REQ_PLAYER)
4671             {
4672               int old_player_nr = setup.network_player_nr;
4673
4674               if (result != -1)
4675                 result = old_player_nr + 1;
4676
4677               switch (key)
4678               {
4679                 case KSYM_space:
4680                   result = old_player_nr + 1;
4681                   break;
4682
4683                 case KSYM_Up:
4684                 case KSYM_1:
4685                   result = 1;
4686                   break;
4687
4688                 case KSYM_Right:
4689                 case KSYM_2:
4690                   result = 2;
4691                   break;
4692
4693                 case KSYM_Down:
4694                 case KSYM_3:
4695                   result = 3;
4696                   break;
4697
4698                 case KSYM_Left:
4699                 case KSYM_4:
4700                   result = 4;
4701                   break;
4702
4703                 default:
4704                   break;
4705               }
4706             }
4707
4708             break;
4709           }
4710
4711           case EVENT_FINGERRELEASE:
4712           case EVENT_KEYRELEASE:
4713             ClearPlayerAction();
4714             break;
4715
4716           case SDL_CONTROLLERBUTTONDOWN:
4717             switch (event.cbutton.button)
4718             {
4719               case SDL_CONTROLLER_BUTTON_A:
4720               case SDL_CONTROLLER_BUTTON_X:
4721               case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4722               case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4723                 result = 1;
4724                 break;
4725
4726               case SDL_CONTROLLER_BUTTON_B:
4727               case SDL_CONTROLLER_BUTTON_Y:
4728               case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4729               case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4730               case SDL_CONTROLLER_BUTTON_BACK:
4731                 result = 0;
4732                 break;
4733             }
4734
4735             if (req_state & REQ_PLAYER)
4736             {
4737               int old_player_nr = setup.network_player_nr;
4738
4739               if (result != -1)
4740                 result = old_player_nr + 1;
4741
4742               switch (event.cbutton.button)
4743               {
4744                 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4745                 case SDL_CONTROLLER_BUTTON_Y:
4746                   result = 1;
4747                   break;
4748
4749                 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4750                 case SDL_CONTROLLER_BUTTON_B:
4751                   result = 2;
4752                   break;
4753
4754                 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4755                 case SDL_CONTROLLER_BUTTON_A:
4756                   result = 3;
4757                   break;
4758
4759                 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4760                 case SDL_CONTROLLER_BUTTON_X:
4761                   result = 4;
4762                   break;
4763
4764                 default:
4765                   break;
4766               }
4767             }
4768
4769             break;
4770
4771           case SDL_CONTROLLERBUTTONUP:
4772             HandleJoystickEvent(&event);
4773             ClearPlayerAction();
4774             break;
4775
4776           default:
4777             HandleOtherEvents(&event);
4778             break;
4779         }
4780       }
4781     }
4782     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4783     {
4784       int joy = AnyJoystick();
4785
4786       if (joy & JOY_BUTTON_1)
4787         result = 1;
4788       else if (joy & JOY_BUTTON_2)
4789         result = 0;
4790     }
4791     else if (AnyJoystick())
4792     {
4793       int joy = AnyJoystick();
4794
4795       if (req_state & REQ_PLAYER)
4796       {
4797         if (joy & JOY_UP)
4798           result = 1;
4799         else if (joy & JOY_RIGHT)
4800           result = 2;
4801         else if (joy & JOY_DOWN)
4802           result = 3;
4803         else if (joy & JOY_LEFT)
4804           result = 4;
4805       }
4806     }
4807
4808     BackToFront();
4809   }
4810
4811   SetDrawtoField(draw_buffer_last);
4812
4813   game.request_active = FALSE;
4814
4815   return result;
4816 }
4817
4818 static boolean RequestDoor(char *text, unsigned int req_state)
4819 {
4820   int draw_buffer_last = GetDrawtoField();
4821   unsigned int old_door_state;
4822   int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4823   int font_nr = FONT_TEXT_2;
4824   char *text_ptr;
4825   int result;
4826   int ty;
4827
4828   if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4829   {
4830     max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4831     font_nr = FONT_TEXT_1;
4832   }
4833
4834   if (game_status == GAME_MODE_PLAYING)
4835     BlitScreenToBitmap(backbuffer);
4836
4837   // disable deactivated drawing when quick-loading level tape recording
4838   if (tape.playing && tape.deactivate_display)
4839     TapeDeactivateDisplayOff(TRUE);
4840
4841   SetMouseCursor(CURSOR_DEFAULT);
4842
4843   // pause network game while waiting for request to answer
4844   if (network.enabled &&
4845       game_status == GAME_MODE_PLAYING &&
4846       !game.all_players_gone &&
4847       req_state & REQUEST_WAIT_FOR_INPUT)
4848     SendToServer_PausePlaying();
4849
4850   old_door_state = GetDoorState();
4851
4852   // simulate releasing mouse button over last gadget, if still pressed
4853   if (button_status)
4854     HandleGadgets(-1, -1, 0);
4855
4856   UnmapAllGadgets();
4857
4858   // draw released gadget before proceeding
4859   // BackToFront();
4860
4861   if (old_door_state & DOOR_OPEN_1)
4862   {
4863     CloseDoor(DOOR_CLOSE_1);
4864
4865     // save old door content
4866     BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4867                0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4868   }
4869
4870   SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4871   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4872
4873   // clear door drawing field
4874   DrawBackground(DX, DY, DXSIZE, DYSIZE);
4875
4876   // force DOOR font inside door area
4877   SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4878
4879   // write text for request
4880   for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4881   {
4882     char text_line[max_request_line_len + 1];
4883     int tx, tl, tc = 0;
4884
4885     if (!*text_ptr)
4886       break;
4887
4888     for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4889     {
4890       tc = *(text_ptr + tx);
4891       // if (!tc || tc == ' ')
4892       if (!tc || tc == ' ' || tc == '?' || tc == '!')
4893         break;
4894     }
4895
4896     if ((tc == '?' || tc == '!') && tl == 0)
4897       tl = 1;
4898
4899     if (!tl)
4900     { 
4901       text_ptr++; 
4902       ty--; 
4903       continue; 
4904     }
4905
4906     strncpy(text_line, text_ptr, tl);
4907     text_line[tl] = 0;
4908
4909     DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4910              DY + 8 + ty * (getFontHeight(font_nr) + 2),
4911              text_line, font_nr);
4912
4913     text_ptr += tl + (tc == ' ' ? 1 : 0);
4914     // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4915   }
4916
4917   ResetFontStatus();
4918
4919   if (req_state & REQ_ASK)
4920   {
4921     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4922     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4923     MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4924     MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4925   }
4926   else if (req_state & REQ_CONFIRM)
4927   {
4928     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4929     MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4930   }
4931   else if (req_state & REQ_PLAYER)
4932   {
4933     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4934     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4935     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4936     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4937   }
4938
4939   // copy request gadgets to door backbuffer
4940   BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4941
4942   OpenDoor(DOOR_OPEN_1);
4943
4944   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4945   {
4946     if (game_status == GAME_MODE_PLAYING)
4947     {
4948       SetPanelBackground();
4949       SetDrawBackgroundMask(REDRAW_DOOR_1);
4950     }
4951     else
4952     {
4953       SetDrawBackgroundMask(REDRAW_FIELD);
4954     }
4955
4956     return FALSE;
4957   }
4958
4959   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4960
4961   // ---------- handle request buttons ----------
4962   result = RequestHandleEvents(req_state, draw_buffer_last);
4963
4964   UnmapToolButtons();
4965
4966   if (!(req_state & REQ_STAY_OPEN))
4967   {
4968     CloseDoor(DOOR_CLOSE_1);
4969
4970     if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4971         (req_state & REQ_REOPEN))
4972       OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4973   }
4974
4975   RemapAllGadgets();
4976
4977   if (game_status == GAME_MODE_PLAYING)
4978   {
4979     SetPanelBackground();
4980     SetDrawBackgroundMask(REDRAW_DOOR_1);
4981   }
4982   else
4983   {
4984     SetDrawBackgroundMask(REDRAW_FIELD);
4985   }
4986
4987   // continue network game after request
4988   if (network.enabled &&
4989       game_status == GAME_MODE_PLAYING &&
4990       !game.all_players_gone &&
4991       req_state & REQUEST_WAIT_FOR_INPUT)
4992     SendToServer_ContinuePlaying();
4993
4994   // restore deactivated drawing when quick-loading level tape recording
4995   if (tape.playing && tape.deactivate_display)
4996     TapeDeactivateDisplayOn();
4997
4998   return result;
4999 }
5000
5001 static boolean RequestEnvelope(char *text, unsigned int req_state)
5002 {
5003   int draw_buffer_last = GetDrawtoField();
5004   int result;
5005
5006   if (game_status == GAME_MODE_PLAYING)
5007     BlitScreenToBitmap(backbuffer);
5008
5009   // disable deactivated drawing when quick-loading level tape recording
5010   if (tape.playing && tape.deactivate_display)
5011     TapeDeactivateDisplayOff(TRUE);
5012
5013   SetMouseCursor(CURSOR_DEFAULT);
5014
5015   // pause network game while waiting for request to answer
5016   if (network.enabled &&
5017       game_status == GAME_MODE_PLAYING &&
5018       !game.all_players_gone &&
5019       req_state & REQUEST_WAIT_FOR_INPUT)
5020     SendToServer_PausePlaying();
5021
5022   // simulate releasing mouse button over last gadget, if still pressed
5023   if (button_status)
5024     HandleGadgets(-1, -1, 0);
5025
5026   UnmapAllGadgets();
5027
5028   // (replace with setting corresponding request background)
5029   // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
5030   // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5031
5032   // clear door drawing field
5033   // DrawBackground(DX, DY, DXSIZE, DYSIZE);
5034
5035   ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
5036
5037   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
5038   {
5039     if (game_status == GAME_MODE_PLAYING)
5040     {
5041       SetPanelBackground();
5042       SetDrawBackgroundMask(REDRAW_DOOR_1);
5043     }
5044     else
5045     {
5046       SetDrawBackgroundMask(REDRAW_FIELD);
5047     }
5048
5049     return FALSE;
5050   }
5051
5052   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
5053
5054   // ---------- handle request buttons ----------
5055   result = RequestHandleEvents(req_state, draw_buffer_last);
5056
5057   UnmapToolButtons();
5058
5059   ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
5060
5061   RemapAllGadgets();
5062
5063   if (game_status == GAME_MODE_PLAYING)
5064   {
5065     SetPanelBackground();
5066     SetDrawBackgroundMask(REDRAW_DOOR_1);
5067   }
5068   else
5069   {
5070     SetDrawBackgroundMask(REDRAW_FIELD);
5071   }
5072
5073   // continue network game after request
5074   if (network.enabled &&
5075       game_status == GAME_MODE_PLAYING &&
5076       !game.all_players_gone &&
5077       req_state & REQUEST_WAIT_FOR_INPUT)
5078     SendToServer_ContinuePlaying();
5079
5080   // restore deactivated drawing when quick-loading level tape recording
5081   if (tape.playing && tape.deactivate_display)
5082     TapeDeactivateDisplayOn();
5083
5084   return result;
5085 }
5086
5087 boolean Request(char *text, unsigned int req_state)
5088 {
5089   boolean overlay_enabled = GetOverlayEnabled();
5090   boolean result;
5091
5092   game.request_active_or_moving = TRUE;
5093
5094   SetOverlayEnabled(FALSE);
5095
5096   if (global.use_envelope_request)
5097     result = RequestEnvelope(text, req_state);
5098   else
5099     result = RequestDoor(text, req_state);
5100
5101   SetOverlayEnabled(overlay_enabled);
5102
5103   game.request_active_or_moving = FALSE;
5104
5105   return result;
5106 }
5107
5108 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
5109 {
5110   const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
5111   const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
5112   int compare_result;
5113
5114   if (dpo1->sort_priority != dpo2->sort_priority)
5115     compare_result = dpo1->sort_priority - dpo2->sort_priority;
5116   else
5117     compare_result = dpo1->nr - dpo2->nr;
5118
5119   return compare_result;
5120 }
5121
5122 void InitGraphicCompatibilityInfo_Doors(void)
5123 {
5124   struct
5125   {
5126     int door_token;
5127     int part_1, part_8;
5128     struct DoorInfo *door;
5129   }
5130   doors[] =
5131   {
5132     { DOOR_1,   IMG_GFX_DOOR_1_PART_1,  IMG_GFX_DOOR_1_PART_8,  &door_1 },
5133     { DOOR_2,   IMG_GFX_DOOR_2_PART_1,  IMG_GFX_DOOR_2_PART_8,  &door_2 },
5134
5135     { -1,       -1,                     -1,                     NULL    }
5136   };
5137   struct Rect door_rect_list[] =
5138   {
5139     { DX, DY, DXSIZE, DYSIZE },
5140     { VX, VY, VXSIZE, VYSIZE }
5141   };
5142   int i, j;
5143
5144   for (i = 0; doors[i].door_token != -1; i++)
5145   {
5146     int door_token = doors[i].door_token;
5147     int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5148     int part_1 = doors[i].part_1;
5149     int part_8 = doors[i].part_8;
5150     int part_2 = part_1 + 1;
5151     int part_3 = part_1 + 2;
5152     struct DoorInfo *door = doors[i].door;
5153     struct Rect *door_rect = &door_rect_list[door_index];
5154     boolean door_gfx_redefined = FALSE;
5155
5156     // check if any door part graphic definitions have been redefined
5157
5158     for (j = 0; door_part_controls[j].door_token != -1; j++)
5159     {
5160       struct DoorPartControlInfo *dpc = &door_part_controls[j];
5161       struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
5162
5163       if (dpc->door_token == door_token && fi->redefined)
5164         door_gfx_redefined = TRUE;
5165     }
5166
5167     // check for old-style door graphic/animation modifications
5168
5169     if (!door_gfx_redefined)
5170     {
5171       if (door->anim_mode & ANIM_STATIC_PANEL)
5172       {
5173         door->panel.step_xoffset = 0;
5174         door->panel.step_yoffset = 0;
5175       }
5176
5177       if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
5178       {
5179         struct GraphicInfo *g_part_1 = &graphic_info[part_1];
5180         struct GraphicInfo *g_part_2 = &graphic_info[part_2];
5181         int num_door_steps, num_panel_steps;
5182
5183         // remove door part graphics other than the two default wings
5184
5185         for (j = 0; door_part_controls[j].door_token != -1; j++)
5186         {
5187           struct DoorPartControlInfo *dpc = &door_part_controls[j];
5188           struct GraphicInfo *g = &graphic_info[dpc->graphic];
5189
5190           if (dpc->graphic >= part_3 &&
5191               dpc->graphic <= part_8)
5192             g->bitmap = NULL;
5193         }
5194
5195         // set graphics and screen positions of the default wings
5196
5197         g_part_1->width  = door_rect->width;
5198         g_part_1->height = door_rect->height;
5199         g_part_2->width  = door_rect->width;
5200         g_part_2->height = door_rect->height;
5201         g_part_2->src_x = door_rect->width;
5202         g_part_2->src_y = g_part_1->src_y;
5203
5204         door->part_2.x = door->part_1.x;
5205         door->part_2.y = door->part_1.y;
5206
5207         if (door->width != -1)
5208         {
5209           g_part_1->width = door->width;
5210           g_part_2->width = door->width;
5211
5212           // special treatment for graphics and screen position of right wing
5213           g_part_2->src_x += door_rect->width - door->width;
5214           door->part_2.x  += door_rect->width - door->width;
5215         }
5216
5217         if (door->height != -1)
5218         {
5219           g_part_1->height = door->height;
5220           g_part_2->height = door->height;
5221
5222           // special treatment for graphics and screen position of bottom wing
5223           g_part_2->src_y += door_rect->height - door->height;
5224           door->part_2.y  += door_rect->height - door->height;
5225         }
5226
5227         // set animation delays for the default wings and panels
5228
5229         door->part_1.step_delay = door->step_delay;
5230         door->part_2.step_delay = door->step_delay;
5231         door->panel.step_delay  = door->step_delay;
5232
5233         // set animation draw order for the default wings
5234
5235         door->part_1.sort_priority = 2; // draw left wing over ...
5236         door->part_2.sort_priority = 1; //          ... right wing
5237
5238         // set animation draw offset for the default wings
5239
5240         if (door->anim_mode & ANIM_HORIZONTAL)
5241         {
5242           door->part_1.step_xoffset = door->step_offset;
5243           door->part_1.step_yoffset = 0;
5244           door->part_2.step_xoffset = door->step_offset * -1;
5245           door->part_2.step_yoffset = 0;
5246
5247           num_door_steps = g_part_1->width / door->step_offset;
5248         }
5249         else    // ANIM_VERTICAL
5250         {
5251           door->part_1.step_xoffset = 0;
5252           door->part_1.step_yoffset = door->step_offset;
5253           door->part_2.step_xoffset = 0;
5254           door->part_2.step_yoffset = door->step_offset * -1;
5255
5256           num_door_steps = g_part_1->height / door->step_offset;
5257         }
5258
5259         // set animation draw offset for the default panels
5260
5261         if (door->step_offset > 1)
5262         {
5263           num_panel_steps = 2 * door_rect->height / door->step_offset;
5264           door->panel.start_step = num_panel_steps - num_door_steps;
5265           door->panel.start_step_closing = door->panel.start_step;
5266         }
5267         else
5268         {
5269           num_panel_steps = door_rect->height / door->step_offset;
5270           door->panel.start_step = num_panel_steps - num_door_steps / 2;
5271           door->panel.start_step_closing = door->panel.start_step;
5272           door->panel.step_delay *= 2;
5273         }
5274       }
5275     }
5276   }
5277 }
5278
5279 void InitDoors(void)
5280 {
5281   int i;
5282
5283   for (i = 0; door_part_controls[i].door_token != -1; i++)
5284   {
5285     struct DoorPartControlInfo *dpc = &door_part_controls[i];
5286     struct DoorPartOrderInfo *dpo = &door_part_order[i];
5287
5288     // initialize "start_step_opening" and "start_step_closing", if needed
5289     if (dpc->pos->start_step_opening == 0 &&
5290         dpc->pos->start_step_closing == 0)
5291     {
5292       // dpc->pos->start_step_opening = dpc->pos->start_step;
5293       dpc->pos->start_step_closing = dpc->pos->start_step;
5294     }
5295
5296     // fill structure for door part draw order (sorted below)
5297     dpo->nr = i;
5298     dpo->sort_priority = dpc->pos->sort_priority;
5299   }
5300
5301   // sort door part controls according to sort_priority and graphic number
5302   qsort(door_part_order, MAX_DOOR_PARTS,
5303         sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5304 }
5305
5306 unsigned int OpenDoor(unsigned int door_state)
5307 {
5308   if (door_state & DOOR_COPY_BACK)
5309   {
5310     if (door_state & DOOR_OPEN_1)
5311       BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5312                  1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5313
5314     if (door_state & DOOR_OPEN_2)
5315       BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5316                  1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5317
5318     door_state &= ~DOOR_COPY_BACK;
5319   }
5320
5321   return MoveDoor(door_state);
5322 }
5323
5324 unsigned int CloseDoor(unsigned int door_state)
5325 {
5326   unsigned int old_door_state = GetDoorState();
5327
5328   if (!(door_state & DOOR_NO_COPY_BACK))
5329   {
5330     if (old_door_state & DOOR_OPEN_1)
5331       BlitBitmap(backbuffer, bitmap_db_door_1,
5332                  DX, DY, DXSIZE, DYSIZE, 0, 0);
5333
5334     if (old_door_state & DOOR_OPEN_2)
5335       BlitBitmap(backbuffer, bitmap_db_door_2,
5336                  VX, VY, VXSIZE, VYSIZE, 0, 0);
5337
5338     door_state &= ~DOOR_NO_COPY_BACK;
5339   }
5340
5341   return MoveDoor(door_state);
5342 }
5343
5344 unsigned int GetDoorState(void)
5345 {
5346   return MoveDoor(DOOR_GET_STATE);
5347 }
5348
5349 unsigned int SetDoorState(unsigned int door_state)
5350 {
5351   return MoveDoor(door_state | DOOR_SET_STATE);
5352 }
5353
5354 static int euclid(int a, int b)
5355 {
5356   return (b ? euclid(b, a % b) : a);
5357 }
5358
5359 unsigned int MoveDoor(unsigned int door_state)
5360 {
5361   struct Rect door_rect_list[] =
5362   {
5363     { DX, DY, DXSIZE, DYSIZE },
5364     { VX, VY, VXSIZE, VYSIZE }
5365   };
5366   static int door1 = DOOR_CLOSE_1;
5367   static int door2 = DOOR_CLOSE_2;
5368   DelayCounter door_delay = { 0 };
5369   int i;
5370
5371   if (door_state == DOOR_GET_STATE)
5372     return (door1 | door2);
5373
5374   if (door_state & DOOR_SET_STATE)
5375   {
5376     if (door_state & DOOR_ACTION_1)
5377       door1 = door_state & DOOR_ACTION_1;
5378     if (door_state & DOOR_ACTION_2)
5379       door2 = door_state & DOOR_ACTION_2;
5380
5381     return (door1 | door2);
5382   }
5383
5384   if (!(door_state & DOOR_FORCE_REDRAW))
5385   {
5386     if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5387       door_state &= ~DOOR_OPEN_1;
5388     else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5389       door_state &= ~DOOR_CLOSE_1;
5390     if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5391       door_state &= ~DOOR_OPEN_2;
5392     else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5393       door_state &= ~DOOR_CLOSE_2;
5394   }
5395
5396   if (global.autoplay_leveldir)
5397   {
5398     door_state |= DOOR_NO_DELAY;
5399     door_state &= ~DOOR_CLOSE_ALL;
5400   }
5401
5402   if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5403     door_state |= DOOR_NO_DELAY;
5404
5405   if (door_state & DOOR_ACTION)
5406   {
5407     boolean door_panel_drawn[NUM_DOORS];
5408     boolean panel_has_doors[NUM_DOORS];
5409     boolean door_part_skip[MAX_DOOR_PARTS];
5410     boolean door_part_done[MAX_DOOR_PARTS];
5411     boolean door_part_done_all;
5412     int num_steps[MAX_DOOR_PARTS];
5413     int max_move_delay = 0;     // delay for complete animations of all doors
5414     int max_step_delay = 0;     // delay (ms) between two animation frames
5415     int num_move_steps = 0;     // number of animation steps for all doors
5416     int max_move_delay_doors_only = 0;  // delay for doors only (no panel)
5417     int num_move_steps_doors_only = 0;  // steps for doors only (no panel)
5418     int start = 0;
5419     int k;
5420
5421     for (i = 0; i < NUM_DOORS; i++)
5422       panel_has_doors[i] = FALSE;
5423
5424     for (i = 0; i < MAX_DOOR_PARTS; i++)
5425     {
5426       struct DoorPartControlInfo *dpc = &door_part_controls[i];
5427       struct GraphicInfo *g = &graphic_info[dpc->graphic];
5428       int door_token = dpc->door_token;
5429
5430       door_part_done[i] = FALSE;
5431       door_part_skip[i] = (!(door_state & door_token) ||
5432                            !g->bitmap);
5433     }
5434
5435     for (i = 0; i < MAX_DOOR_PARTS; i++)
5436     {
5437       int nr = door_part_order[i].nr;
5438       struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5439       struct DoorPartPosInfo *pos = dpc->pos;
5440       struct GraphicInfo *g = &graphic_info[dpc->graphic];
5441       int door_token = dpc->door_token;
5442       int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5443       boolean is_panel = DOOR_PART_IS_PANEL(nr);
5444       int step_xoffset = ABS(pos->step_xoffset);
5445       int step_yoffset = ABS(pos->step_yoffset);
5446       int step_delay = pos->step_delay;
5447       int current_door_state = door_state & door_token;
5448       boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
5449       boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5450       boolean part_opening = (is_panel ? door_closing : door_opening);
5451       int start_step = (part_opening ? pos->start_step_opening :
5452                         pos->start_step_closing);
5453       float move_xsize = (step_xoffset ? g->width  : 0);
5454       float move_ysize = (step_yoffset ? g->height : 0);
5455       int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5456       int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5457       int move_steps = (move_xsteps && move_ysteps ?
5458                         MIN(move_xsteps, move_ysteps) :
5459                         move_xsteps ? move_xsteps : move_ysteps) - start_step;
5460       int move_delay = move_steps * step_delay;
5461
5462       if (door_part_skip[nr])
5463         continue;
5464
5465       max_move_delay = MAX(max_move_delay, move_delay);
5466       max_step_delay = (max_step_delay == 0 ? step_delay :
5467                         euclid(max_step_delay, step_delay));
5468       num_steps[nr] = move_steps;
5469
5470       if (!is_panel)
5471       {
5472         max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5473
5474         panel_has_doors[door_index] = TRUE;
5475       }
5476     }
5477
5478     max_step_delay = MAX(1, max_step_delay);    // prevent division by zero
5479
5480     num_move_steps = max_move_delay / max_step_delay;
5481     num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5482
5483     door_delay.value = max_step_delay;
5484
5485     if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5486     {
5487       start = num_move_steps - 1;
5488     }
5489     else
5490     {
5491       // opening door sound has priority over simultaneously closing door
5492       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5493       {
5494         PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5495
5496         if (door_state & DOOR_OPEN_1)
5497           PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5498         if (door_state & DOOR_OPEN_2)
5499           PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5500       }
5501       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5502       {
5503         PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5504
5505         if (door_state & DOOR_CLOSE_1)
5506           PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5507         if (door_state & DOOR_CLOSE_2)
5508           PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5509       }
5510     }
5511
5512     for (k = start; k < num_move_steps; k++)
5513     {
5514       int last_frame = num_move_steps - 1;      // last frame of this "for" loop
5515
5516       door_part_done_all = TRUE;
5517
5518       for (i = 0; i < NUM_DOORS; i++)
5519         door_panel_drawn[i] = FALSE;
5520
5521       for (i = 0; i < MAX_DOOR_PARTS; i++)
5522       {
5523         int nr = door_part_order[i].nr;
5524         struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5525         struct DoorPartPosInfo *pos = dpc->pos;
5526         struct GraphicInfo *g = &graphic_info[dpc->graphic];
5527         int door_token = dpc->door_token;
5528         int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5529         boolean is_panel = DOOR_PART_IS_PANEL(nr);
5530         boolean is_panel_and_door_has_closed = FALSE;
5531         struct Rect *door_rect = &door_rect_list[door_index];
5532         Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5533                                   bitmap_db_door_2);
5534         Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5535         int current_door_state = door_state & door_token;
5536         boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
5537         boolean door_closing = !door_opening;
5538         boolean part_opening = (is_panel ? door_closing : door_opening);
5539         boolean part_closing = !part_opening;
5540         int start_step = (part_opening ? pos->start_step_opening :
5541                           pos->start_step_closing);
5542         int step_delay = pos->step_delay;
5543         int step_factor = step_delay / max_step_delay;
5544         int k1 = (step_factor ? k / step_factor + 1 : k);
5545         int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5546         int kk = MAX(0, k2);
5547         int g_src_x = 0;
5548         int g_src_y = 0;
5549         int src_x, src_y, src_xx, src_yy;
5550         int dst_x, dst_y, dst_xx, dst_yy;
5551         int width, height;
5552
5553         if (door_part_skip[nr])
5554           continue;
5555
5556         if (!(door_state & door_token))
5557           continue;
5558
5559         if (!g->bitmap)
5560           continue;
5561
5562         if (!is_panel)
5563         {
5564           int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5565           int kk_door = MAX(0, k2_door);
5566           int sync_frame = kk_door * door_delay.value;
5567           int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5568
5569           getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5570                                 &g_src_x, &g_src_y);
5571         }
5572
5573         // draw door panel
5574
5575         if (!door_panel_drawn[door_index])
5576         {
5577           ClearRectangle(drawto, door_rect->x, door_rect->y,
5578                          door_rect->width, door_rect->height);
5579
5580           door_panel_drawn[door_index] = TRUE;
5581         }
5582
5583         // draw opening or closing door parts
5584
5585         if (pos->step_xoffset < 0)      // door part on right side
5586         {
5587           src_xx = 0;
5588           dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5589           width = g->width;
5590
5591           if (dst_xx + width > door_rect->width)
5592             width = door_rect->width - dst_xx;
5593         }
5594         else                            // door part on left side
5595         {
5596           src_xx = 0;
5597           dst_xx = pos->x - kk * pos->step_xoffset;
5598
5599           if (dst_xx < 0)
5600           {
5601             src_xx = ABS(dst_xx);
5602             dst_xx = 0;
5603           }
5604
5605           width = g->width - src_xx;
5606
5607           if (width > door_rect->width)
5608             width = door_rect->width;
5609
5610           // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
5611         }
5612
5613         if (pos->step_yoffset < 0)      // door part on bottom side
5614         {
5615           src_yy = 0;
5616           dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5617           height = g->height;
5618
5619           if (dst_yy + height > door_rect->height)
5620             height = door_rect->height - dst_yy;
5621         }
5622         else                            // door part on top side
5623         {
5624           src_yy = 0;
5625           dst_yy = pos->y - kk * pos->step_yoffset;
5626
5627           if (dst_yy < 0)
5628           {
5629             src_yy = ABS(dst_yy);
5630             dst_yy = 0;
5631           }
5632
5633           height = g->height - src_yy;
5634         }
5635
5636         src_x = g_src_x + src_xx;
5637         src_y = g_src_y + src_yy;
5638
5639         dst_x = door_rect->x + dst_xx;
5640         dst_y = door_rect->y + dst_yy;
5641
5642         is_panel_and_door_has_closed =
5643           (is_panel &&
5644            door_closing &&
5645            panel_has_doors[door_index] &&
5646            k >= num_move_steps_doors_only - 1);
5647
5648         if (width  >= 0 && width  <= g->width &&
5649             height >= 0 && height <= g->height &&
5650             !is_panel_and_door_has_closed)
5651         {
5652           if (is_panel || !pos->draw_masked)
5653             BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5654                        dst_x, dst_y);
5655           else
5656             BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5657                              dst_x, dst_y);
5658         }
5659
5660         redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5661
5662         if ((part_opening && (width < 0         || height < 0)) ||
5663             (part_closing && (width >= g->width && height >= g->height)))
5664           door_part_done[nr] = TRUE;
5665
5666         // continue door part animations, but not panel after door has closed
5667         if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5668           door_part_done_all = FALSE;
5669       }
5670
5671       if (!(door_state & DOOR_NO_DELAY))
5672       {
5673         BackToFront();
5674
5675         SkipUntilDelayReached(&door_delay, &k, last_frame);
5676
5677         // prevent OS (Windows) from complaining about program not responding
5678         CheckQuitEvent();
5679       }
5680
5681       if (door_part_done_all)
5682         break;
5683     }
5684
5685     if (!(door_state & DOOR_NO_DELAY))
5686     {
5687       // wait for specified door action post delay
5688       if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5689         door_delay.value = MAX(door_1.post_delay, door_2.post_delay);
5690       else if (door_state & DOOR_ACTION_1)
5691         door_delay.value = door_1.post_delay;
5692       else if (door_state & DOOR_ACTION_2)
5693         door_delay.value = door_2.post_delay;
5694
5695       while (!DelayReached(&door_delay))
5696         BackToFront();
5697     }
5698   }
5699
5700   if (door_state & DOOR_ACTION_1)
5701     door1 = door_state & DOOR_ACTION_1;
5702   if (door_state & DOOR_ACTION_2)
5703     door2 = door_state & DOOR_ACTION_2;
5704
5705   // draw masked border over door area
5706   DrawMaskedBorder(REDRAW_DOOR_1);
5707   DrawMaskedBorder(REDRAW_DOOR_2);
5708
5709   ClearAutoRepeatKeyEvents();
5710
5711   return (door1 | door2);
5712 }
5713
5714 static boolean useSpecialEditorDoor(void)
5715 {
5716   int graphic = IMG_GLOBAL_BORDER_EDITOR;
5717   boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5718
5719   // do not draw special editor door if editor border defined or redefined
5720   if (graphic_info[graphic].bitmap != NULL || redefined)
5721     return FALSE;
5722
5723   // do not draw special editor door if global border defined to be empty
5724   if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5725     return FALSE;
5726
5727   // do not draw special editor door if viewport definitions do not match
5728   if (EX != VX ||
5729       EY >= VY ||
5730       EXSIZE != VXSIZE ||
5731       EY + EYSIZE != VY + VYSIZE)
5732     return FALSE;
5733
5734   return TRUE;
5735 }
5736
5737 void DrawSpecialEditorDoor(void)
5738 {
5739   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5740   int top_border_width = gfx1->width;
5741   int top_border_height = gfx1->height;
5742   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5743   int ex = EX - outer_border;
5744   int ey = EY - outer_border;
5745   int vy = VY - outer_border;
5746   int exsize = EXSIZE + 2 * outer_border;
5747
5748   if (!useSpecialEditorDoor())
5749     return;
5750
5751   // draw bigger level editor toolbox window
5752   BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5753              top_border_width, top_border_height, ex, ey - top_border_height);
5754   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5755              exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5756
5757   redraw_mask |= REDRAW_ALL;
5758 }
5759
5760 void UndrawSpecialEditorDoor(void)
5761 {
5762   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5763   int top_border_width = gfx1->width;
5764   int top_border_height = gfx1->height;
5765   int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5766   int ex = EX - outer_border;
5767   int ey = EY - outer_border;
5768   int ey_top = ey - top_border_height;
5769   int exsize = EXSIZE + 2 * outer_border;
5770   int eysize = EYSIZE + 2 * outer_border;
5771
5772   if (!useSpecialEditorDoor())
5773     return;
5774
5775   // draw normal tape recorder window
5776   if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5777   {
5778     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5779                ex, ey_top, top_border_width, top_border_height,
5780                ex, ey_top);
5781     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5782                ex, ey, exsize, eysize, ex, ey);
5783   }
5784   else
5785   {
5786     // if screen background is set to "[NONE]", clear editor toolbox window
5787     ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5788     ClearRectangle(drawto, ex, ey, exsize, eysize);
5789   }
5790
5791   redraw_mask |= REDRAW_ALL;
5792 }
5793
5794
5795 // ---------- new tool button stuff -------------------------------------------
5796
5797 static struct
5798 {
5799   int graphic;
5800   struct TextPosInfo *pos;
5801   int gadget_id;
5802   boolean is_touch_button;
5803   char *infotext;
5804 } toolbutton_info[NUM_TOOL_BUTTONS] =
5805 {
5806   {
5807     IMG_GFX_REQUEST_BUTTON_YES,         &request.button.yes,
5808     TOOL_CTRL_ID_YES, FALSE,            "yes"
5809   },
5810   {
5811     IMG_GFX_REQUEST_BUTTON_NO,          &request.button.no,
5812     TOOL_CTRL_ID_NO, FALSE,             "no"
5813   },
5814   {
5815     IMG_GFX_REQUEST_BUTTON_CONFIRM,     &request.button.confirm,
5816     TOOL_CTRL_ID_CONFIRM, FALSE,        "confirm"
5817   },
5818   {
5819     IMG_GFX_REQUEST_BUTTON_PLAYER_1,    &request.button.player_1,
5820     TOOL_CTRL_ID_PLAYER_1, FALSE,       "player 1"
5821   },
5822   {
5823     IMG_GFX_REQUEST_BUTTON_PLAYER_2,    &request.button.player_2,
5824     TOOL_CTRL_ID_PLAYER_2, FALSE,       "player 2"
5825   },
5826   {
5827     IMG_GFX_REQUEST_BUTTON_PLAYER_3,    &request.button.player_3,
5828     TOOL_CTRL_ID_PLAYER_3, FALSE,       "player 3"
5829   },
5830   {
5831     IMG_GFX_REQUEST_BUTTON_PLAYER_4,    &request.button.player_4,
5832     TOOL_CTRL_ID_PLAYER_4, FALSE,       "player 4"
5833   },
5834   {
5835     IMG_GFX_REQUEST_BUTTON_TOUCH_YES,   &request.button.touch_yes,
5836     TOOL_CTRL_ID_TOUCH_YES, TRUE,       "yes"
5837   },
5838   {
5839     IMG_GFX_REQUEST_BUTTON_TOUCH_NO,    &request.button.touch_no,
5840     TOOL_CTRL_ID_TOUCH_NO, TRUE,        "no"
5841   },
5842   {
5843     IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5844     TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE,   "confirm"
5845   }
5846 };
5847
5848 void CreateToolButtons(void)
5849 {
5850   int i;
5851
5852   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5853   {
5854     int graphic = toolbutton_info[i].graphic;
5855     struct GraphicInfo *gfx = &graphic_info[graphic];
5856     struct TextPosInfo *pos = toolbutton_info[i].pos;
5857     struct GadgetInfo *gi;
5858     Bitmap *deco_bitmap = None;
5859     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5860     unsigned int event_mask = GD_EVENT_RELEASED;
5861     boolean is_touch_button = toolbutton_info[i].is_touch_button;
5862     int base_x = (is_touch_button ? 0 : DX);
5863     int base_y = (is_touch_button ? 0 : DY);
5864     int gd_x = gfx->src_x;
5865     int gd_y = gfx->src_y;
5866     int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5867     int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5868     int x = pos->x;
5869     int y = pos->y;
5870     int id = i;
5871
5872     // do not use touch buttons if overlay touch buttons are disabled
5873     if (is_touch_button && !setup.touch.overlay_buttons)
5874       continue;
5875
5876     if (global.use_envelope_request && !is_touch_button)
5877     {
5878       setRequestPosition(&base_x, &base_y, TRUE);
5879
5880       // check if request buttons are outside of envelope and fix, if needed
5881       if (x < 0 || x + gfx->width  > request.width ||
5882           y < 0 || y + gfx->height > request.height)
5883       {
5884         if (id == TOOL_CTRL_ID_YES)
5885         {
5886           x = 0;
5887           y = request.height - 2 * request.border_size - gfx->height;
5888         }
5889         else if (id == TOOL_CTRL_ID_NO)
5890         {
5891           x = request.width  - 2 * request.border_size - gfx->width;
5892           y = request.height - 2 * request.border_size - gfx->height;
5893         }
5894         else if (id == TOOL_CTRL_ID_CONFIRM)
5895         {
5896           x = (request.width - 2 * request.border_size - gfx->width) / 2;
5897           y = request.height - 2 * request.border_size - gfx->height;
5898         }
5899         else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5900         {
5901           int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5902
5903           x = (request.width - 2 * request.border_size - gfx->width) / 2;
5904           y = request.height - 2 * request.border_size - gfx->height * 2;
5905
5906           x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5907           y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5908         }
5909       }
5910     }
5911
5912     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4 &&
5913         pos->draw_player)
5914     {
5915       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5916
5917       getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5918                             pos->size, &deco_bitmap, &deco_x, &deco_y);
5919       deco_xpos = (gfx->width  - pos->size) / 2;
5920       deco_ypos = (gfx->height - pos->size) / 2;
5921     }
5922
5923     gi = CreateGadget(GDI_CUSTOM_ID, id,
5924                       GDI_IMAGE_ID, graphic,
5925                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
5926                       GDI_X, base_x + x,
5927                       GDI_Y, base_y + y,
5928                       GDI_WIDTH, gfx->width,
5929                       GDI_HEIGHT, gfx->height,
5930                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5931                       GDI_STATE, GD_BUTTON_UNPRESSED,
5932                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5933                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5934                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5935                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5936                       GDI_DECORATION_SIZE, pos->size, pos->size,
5937                       GDI_DECORATION_SHIFTING, 1, 1,
5938                       GDI_DIRECT_DRAW, FALSE,
5939                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5940                       GDI_EVENT_MASK, event_mask,
5941                       GDI_CALLBACK_ACTION, HandleToolButtons,
5942                       GDI_END);
5943
5944     if (gi == NULL)
5945       Fail("cannot create gadget");
5946
5947     tool_gadget[id] = gi;
5948   }
5949 }
5950
5951 void FreeToolButtons(void)
5952 {
5953   int i;
5954
5955   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5956     FreeGadget(tool_gadget[i]);
5957 }
5958
5959 static void UnmapToolButtons(void)
5960 {
5961   int i;
5962
5963   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5964     UnmapGadget(tool_gadget[i]);
5965 }
5966
5967 static void HandleToolButtons(struct GadgetInfo *gi)
5968 {
5969   request_gadget_id = gi->custom_id;
5970 }
5971
5972 static struct Mapping_EM_to_RND_object
5973 {
5974   int element_em;
5975   boolean is_rnd_to_em_mapping;         // unique mapping EM <-> RND
5976   boolean is_backside;                  // backside of moving element
5977
5978   int element_rnd;
5979   int action;
5980   int direction;
5981 }
5982 em_object_mapping_list[GAME_TILE_MAX + 1] =
5983 {
5984   {
5985     Zborder,                            FALSE,  FALSE,
5986     EL_EMPTY,                           -1, -1
5987   },
5988   {
5989     Zplayer,                            FALSE,  FALSE,
5990     EL_EMPTY,                           -1, -1
5991   },
5992
5993   {
5994     Zbug,                               FALSE,  FALSE,
5995     EL_EMPTY,                           -1, -1
5996   },
5997   {
5998     Ztank,                              FALSE,  FALSE,
5999     EL_EMPTY,                           -1, -1
6000   },
6001   {
6002     Zeater,                             FALSE,  FALSE,
6003     EL_EMPTY,                           -1, -1
6004   },
6005   {
6006     Zdynamite,                          FALSE,  FALSE,
6007     EL_EMPTY,                           -1, -1
6008   },
6009   {
6010     Zboom,                              FALSE,  FALSE,
6011     EL_EMPTY,                           -1, -1
6012   },
6013
6014   {
6015     Xchain,                             FALSE,  FALSE,
6016     EL_DEFAULT,                         ACTION_EXPLODING, -1
6017   },
6018   {
6019     Xboom_bug,                          FALSE,  FALSE,
6020     EL_BUG,                             ACTION_EXPLODING, -1
6021   },
6022   {
6023     Xboom_tank,                         FALSE,  FALSE,
6024     EL_SPACESHIP,                       ACTION_EXPLODING, -1
6025   },
6026   {
6027     Xboom_android,                      FALSE,  FALSE,
6028     EL_EMC_ANDROID,                     ACTION_OTHER, -1
6029   },
6030   {
6031     Xboom_1,                            FALSE,  FALSE,
6032     EL_DEFAULT,                         ACTION_EXPLODING, -1
6033   },
6034   {
6035     Xboom_2,                            FALSE,  FALSE,
6036     EL_DEFAULT,                         ACTION_EXPLODING, -1
6037   },
6038
6039   {
6040     Xblank,                             TRUE,   FALSE,
6041     EL_EMPTY,                           -1, -1
6042   },
6043
6044   {
6045     Xsplash_e,                          FALSE,  FALSE,
6046     EL_ACID_SPLASH_RIGHT,               -1, -1
6047   },
6048   {
6049     Xsplash_w,                          FALSE,  FALSE,
6050     EL_ACID_SPLASH_LEFT,                -1, -1
6051   },
6052
6053   {
6054     Xplant,                             TRUE,   FALSE,
6055     EL_EMC_PLANT,                       -1, -1
6056   },
6057   {
6058     Yplant,                             FALSE,  FALSE,
6059     EL_EMC_PLANT,                       -1, -1
6060   },
6061
6062   {
6063     Xacid_1,                            TRUE,   FALSE,
6064     EL_ACID,                            -1, -1
6065   },
6066   {
6067     Xacid_2,                            FALSE,  FALSE,
6068     EL_ACID,                            -1, -1
6069   },
6070   {
6071     Xacid_3,                            FALSE,  FALSE,
6072     EL_ACID,                            -1, -1
6073   },
6074   {
6075     Xacid_4,                            FALSE,  FALSE,
6076     EL_ACID,                            -1, -1
6077   },
6078   {
6079     Xacid_5,                            FALSE,  FALSE,
6080     EL_ACID,                            -1, -1
6081   },
6082   {
6083     Xacid_6,                            FALSE,  FALSE,
6084     EL_ACID,                            -1, -1
6085   },
6086   {
6087     Xacid_7,                            FALSE,  FALSE,
6088     EL_ACID,                            -1, -1
6089   },
6090   {
6091     Xacid_8,                            FALSE,  FALSE,
6092     EL_ACID,                            -1, -1
6093   },
6094
6095   {
6096     Xfake_acid_1,                       TRUE,   FALSE,
6097     EL_EMC_FAKE_ACID,                   -1, -1
6098   },
6099   {
6100     Xfake_acid_2,                       FALSE,  FALSE,
6101     EL_EMC_FAKE_ACID,                   -1, -1
6102   },
6103   {
6104     Xfake_acid_3,                       FALSE,  FALSE,
6105     EL_EMC_FAKE_ACID,                   -1, -1
6106   },
6107   {
6108     Xfake_acid_4,                       FALSE,  FALSE,
6109     EL_EMC_FAKE_ACID,                   -1, -1
6110   },
6111   {
6112     Xfake_acid_5,                       FALSE,  FALSE,
6113     EL_EMC_FAKE_ACID,                   -1, -1
6114   },
6115   {
6116     Xfake_acid_6,                       FALSE,  FALSE,
6117     EL_EMC_FAKE_ACID,                   -1, -1
6118   },
6119   {
6120     Xfake_acid_7,                       FALSE,  FALSE,
6121     EL_EMC_FAKE_ACID,                   -1, -1
6122   },
6123   {
6124     Xfake_acid_8,                       FALSE,  FALSE,
6125     EL_EMC_FAKE_ACID,                   -1, -1
6126   },
6127
6128   {
6129     Xfake_acid_1_player,                FALSE,  FALSE,
6130     EL_EMC_FAKE_ACID,                   -1, -1
6131   },
6132   {
6133     Xfake_acid_2_player,                FALSE,  FALSE,
6134     EL_EMC_FAKE_ACID,                   -1, -1
6135   },
6136   {
6137     Xfake_acid_3_player,                FALSE,  FALSE,
6138     EL_EMC_FAKE_ACID,                   -1, -1
6139   },
6140   {
6141     Xfake_acid_4_player,                FALSE,  FALSE,
6142     EL_EMC_FAKE_ACID,                   -1, -1
6143   },
6144   {
6145     Xfake_acid_5_player,                FALSE,  FALSE,
6146     EL_EMC_FAKE_ACID,                   -1, -1
6147   },
6148   {
6149     Xfake_acid_6_player,                FALSE,  FALSE,
6150     EL_EMC_FAKE_ACID,                   -1, -1
6151   },
6152   {
6153     Xfake_acid_7_player,                FALSE,  FALSE,
6154     EL_EMC_FAKE_ACID,                   -1, -1
6155   },
6156   {
6157     Xfake_acid_8_player,                FALSE,  FALSE,
6158     EL_EMC_FAKE_ACID,                   -1, -1
6159   },
6160
6161   {
6162     Xgrass,                             TRUE,   FALSE,
6163     EL_EMC_GRASS,                       -1, -1
6164   },
6165   {
6166     Ygrass_nB,                          FALSE,  FALSE,
6167     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_UP
6168   },
6169   {
6170     Ygrass_eB,                          FALSE,  FALSE,
6171     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_RIGHT
6172   },
6173   {
6174     Ygrass_sB,                          FALSE,  FALSE,
6175     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_DOWN
6176   },
6177   {
6178     Ygrass_wB,                          FALSE,  FALSE,
6179     EL_EMC_GRASS,                       ACTION_DIGGING, MV_BIT_LEFT
6180   },
6181
6182   {
6183     Xdirt,                              TRUE,   FALSE,
6184     EL_SAND,                            -1, -1
6185   },
6186   {
6187     Ydirt_nB,                           FALSE,  FALSE,
6188     EL_SAND,                            ACTION_DIGGING, MV_BIT_UP
6189   },
6190   {
6191     Ydirt_eB,                           FALSE,  FALSE,
6192     EL_SAND,                            ACTION_DIGGING, MV_BIT_RIGHT
6193   },
6194   {
6195     Ydirt_sB,                           FALSE,  FALSE,
6196     EL_SAND,                            ACTION_DIGGING, MV_BIT_DOWN
6197   },
6198   {
6199     Ydirt_wB,                           FALSE,  FALSE,
6200     EL_SAND,                            ACTION_DIGGING, MV_BIT_LEFT
6201   },
6202
6203   {
6204     Xandroid,                           TRUE,   FALSE,
6205     EL_EMC_ANDROID,                     ACTION_ACTIVE, -1
6206   },
6207   {
6208     Xandroid_1_n,                       FALSE,  FALSE,
6209     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
6210   },
6211   {
6212     Xandroid_2_n,                       FALSE,  FALSE,
6213     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_UP
6214   },
6215   {
6216     Xandroid_1_e,                       FALSE,  FALSE,
6217     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
6218   },
6219   {
6220     Xandroid_2_e,                       FALSE,  FALSE,
6221     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_RIGHT
6222   },
6223   {
6224     Xandroid_1_w,                       FALSE,  FALSE,
6225     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
6226   },
6227   {
6228     Xandroid_2_w,                       FALSE,  FALSE,
6229     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_LEFT
6230   },
6231   {
6232     Xandroid_1_s,                       FALSE,  FALSE,
6233     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
6234   },
6235   {
6236     Xandroid_2_s,                       FALSE,  FALSE,
6237     EL_EMC_ANDROID,                     ACTION_ACTIVE, MV_BIT_DOWN
6238   },
6239   {
6240     Yandroid_n,                         FALSE,  FALSE,
6241     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
6242   },
6243   {
6244     Yandroid_nB,                        FALSE,  TRUE,
6245     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_UP
6246   },
6247   {
6248     Yandroid_ne,                        FALSE,  FALSE,
6249     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPRIGHT
6250   },
6251   {
6252     Yandroid_neB,                       FALSE,  TRUE,
6253     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPRIGHT
6254   },
6255   {
6256     Yandroid_e,                         FALSE,  FALSE,
6257     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
6258   },
6259   {
6260     Yandroid_eB,                        FALSE,  TRUE,
6261     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_RIGHT
6262   },
6263   {
6264     Yandroid_se,                        FALSE,  FALSE,
6265     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNRIGHT
6266   },
6267   {
6268     Yandroid_seB,                       FALSE,  TRUE,
6269     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6270   },
6271   {
6272     Yandroid_s,                         FALSE,  FALSE,
6273     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
6274   },
6275   {
6276     Yandroid_sB,                        FALSE,  TRUE,
6277     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_DOWN
6278   },
6279   {
6280     Yandroid_sw,                        FALSE,  FALSE,
6281     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_DOWNLEFT
6282   },
6283   {
6284     Yandroid_swB,                       FALSE,  TRUE,
6285     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_DOWNLEFT
6286   },
6287   {
6288     Yandroid_w,                         FALSE,  FALSE,
6289     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
6290   },
6291   {
6292     Yandroid_wB,                        FALSE,  TRUE,
6293     EL_EMC_ANDROID,                     ACTION_MOVING, MV_BIT_LEFT
6294   },
6295   {
6296     Yandroid_nw,                        FALSE,  FALSE,
6297     EL_EMC_ANDROID,                     ACTION_GROWING, MV_BIT_UPLEFT
6298   },
6299   {
6300     Yandroid_nwB,                       FALSE,  TRUE,
6301     EL_EMC_ANDROID,                     ACTION_SHRINKING, MV_BIT_UPLEFT
6302   },
6303
6304   {
6305     Xeater_n,                           TRUE,   FALSE,
6306     EL_YAMYAM_UP,                       -1, -1
6307   },
6308   {
6309     Xeater_e,                           TRUE,   FALSE,
6310     EL_YAMYAM_RIGHT,                    -1, -1
6311   },
6312   {
6313     Xeater_w,                           TRUE,   FALSE,
6314     EL_YAMYAM_LEFT,                     -1, -1
6315   },
6316   {
6317     Xeater_s,                           TRUE,   FALSE,
6318     EL_YAMYAM_DOWN,                     -1, -1
6319   },
6320   {
6321     Yeater_n,                           FALSE,  FALSE,
6322     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
6323   },
6324   {
6325     Yeater_nB,                          FALSE,  TRUE,
6326     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_UP
6327   },
6328   {
6329     Yeater_e,                           FALSE,  FALSE,
6330     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
6331   },
6332   {
6333     Yeater_eB,                          FALSE,  TRUE,
6334     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_RIGHT
6335   },
6336   {
6337     Yeater_s,                           FALSE,  FALSE,
6338     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
6339   },
6340   {
6341     Yeater_sB,                          FALSE,  TRUE,
6342     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_DOWN
6343   },
6344   {
6345     Yeater_w,                           FALSE,  FALSE,
6346     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
6347   },
6348   {
6349     Yeater_wB,                          FALSE,  TRUE,
6350     EL_YAMYAM,                          ACTION_MOVING, MV_BIT_LEFT
6351   },
6352   {
6353     Yeater_stone,                       FALSE,  FALSE,
6354     EL_YAMYAM,                          ACTION_SMASHED_BY_ROCK, -1
6355   },
6356   {
6357     Yeater_spring,                      FALSE,  FALSE,
6358     EL_YAMYAM,                          ACTION_SMASHED_BY_SPRING, -1
6359   },
6360
6361   {
6362     Xalien,                             TRUE,   FALSE,
6363     EL_ROBOT,                           -1, -1
6364   },
6365   {
6366     Xalien_pause,                       FALSE,  FALSE,
6367     EL_ROBOT,                           -1, -1
6368   },
6369   {
6370     Yalien_n,                           FALSE,  FALSE,
6371     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
6372   },
6373   {
6374     Yalien_nB,                          FALSE,  TRUE,
6375     EL_ROBOT,                           ACTION_MOVING, MV_BIT_UP
6376   },
6377   {
6378     Yalien_e,                           FALSE,  FALSE,
6379     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
6380   },
6381   {
6382     Yalien_eB,                          FALSE,  TRUE,
6383     EL_ROBOT,                           ACTION_MOVING, MV_BIT_RIGHT
6384   },
6385   {
6386     Yalien_s,                           FALSE,  FALSE,
6387     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
6388   },
6389   {
6390     Yalien_sB,                          FALSE,  TRUE,
6391     EL_ROBOT,                           ACTION_MOVING, MV_BIT_DOWN
6392   },
6393   {
6394     Yalien_w,                           FALSE,  FALSE,
6395     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
6396   },
6397   {
6398     Yalien_wB,                          FALSE,  TRUE,
6399     EL_ROBOT,                           ACTION_MOVING, MV_BIT_LEFT
6400   },
6401   {
6402     Yalien_stone,                       FALSE,  FALSE,
6403     EL_ROBOT,                           ACTION_SMASHED_BY_ROCK, -1
6404   },
6405   {
6406     Yalien_spring,                      FALSE,  FALSE,
6407     EL_ROBOT,                           ACTION_SMASHED_BY_SPRING, -1
6408   },
6409
6410   {
6411     Xbug_1_n,                           TRUE,   FALSE,
6412     EL_BUG_UP,                          -1, -1
6413   },
6414   {
6415     Xbug_1_e,                           TRUE,   FALSE,
6416     EL_BUG_RIGHT,                       -1, -1
6417   },
6418   {
6419     Xbug_1_s,                           TRUE,   FALSE,
6420     EL_BUG_DOWN,                        -1, -1
6421   },
6422   {
6423     Xbug_1_w,                           TRUE,   FALSE,
6424     EL_BUG_LEFT,                        -1, -1
6425   },
6426   {
6427     Xbug_2_n,                           FALSE,  FALSE,
6428     EL_BUG_UP,                          -1, -1
6429   },
6430   {
6431     Xbug_2_e,                           FALSE,  FALSE,
6432     EL_BUG_RIGHT,                       -1, -1
6433   },
6434   {
6435     Xbug_2_s,                           FALSE,  FALSE,
6436     EL_BUG_DOWN,                        -1, -1
6437   },
6438   {
6439     Xbug_2_w,                           FALSE,  FALSE,
6440     EL_BUG_LEFT,                        -1, -1
6441   },
6442   {
6443     Ybug_n,                             FALSE,  FALSE,
6444     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
6445   },
6446   {
6447     Ybug_nB,                            FALSE,  TRUE,
6448     EL_BUG,                             ACTION_MOVING, MV_BIT_UP
6449   },
6450   {
6451     Ybug_e,                             FALSE,  FALSE,
6452     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
6453   },
6454   {
6455     Ybug_eB,                            FALSE,  TRUE,
6456     EL_BUG,                             ACTION_MOVING, MV_BIT_RIGHT
6457   },
6458   {
6459     Ybug_s,                             FALSE,  FALSE,
6460     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
6461   },
6462   {
6463     Ybug_sB,                            FALSE,  TRUE,
6464     EL_BUG,                             ACTION_MOVING, MV_BIT_DOWN
6465   },
6466   {
6467     Ybug_w,                             FALSE,  FALSE,
6468     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
6469   },
6470   {
6471     Ybug_wB,                            FALSE,  TRUE,
6472     EL_BUG,                             ACTION_MOVING, MV_BIT_LEFT
6473   },
6474   {
6475     Ybug_w_n,                           FALSE,  FALSE,
6476     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6477   },
6478   {
6479     Ybug_n_e,                           FALSE,  FALSE,
6480     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6481   },
6482   {
6483     Ybug_e_s,                           FALSE,  FALSE,
6484     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6485   },
6486   {
6487     Ybug_s_w,                           FALSE,  FALSE,
6488     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6489   },
6490   {
6491     Ybug_e_n,                           FALSE,  FALSE,
6492     EL_BUG,                             ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6493   },
6494   {
6495     Ybug_s_e,                           FALSE,  FALSE,
6496     EL_BUG,                             ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6497   },
6498   {
6499     Ybug_w_s,                           FALSE,  FALSE,
6500     EL_BUG,                             ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6501   },
6502   {
6503     Ybug_n_w,                           FALSE,  FALSE,
6504     EL_BUG,                             ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6505   },
6506   {
6507     Ybug_stone,                         FALSE,  FALSE,
6508     EL_BUG,                             ACTION_SMASHED_BY_ROCK, -1
6509   },
6510   {
6511     Ybug_spring,                        FALSE,  FALSE,
6512     EL_BUG,                             ACTION_SMASHED_BY_SPRING, -1
6513   },
6514
6515   {
6516     Xtank_1_n,                          TRUE,   FALSE,
6517     EL_SPACESHIP_UP,                    -1, -1
6518   },
6519   {
6520     Xtank_1_e,                          TRUE,   FALSE,
6521     EL_SPACESHIP_RIGHT,                 -1, -1
6522   },
6523   {
6524     Xtank_1_s,                          TRUE,   FALSE,
6525     EL_SPACESHIP_DOWN,                  -1, -1
6526   },
6527   {
6528     Xtank_1_w,                          TRUE,   FALSE,
6529     EL_SPACESHIP_LEFT,                  -1, -1
6530   },
6531   {
6532     Xtank_2_n,                          FALSE,  FALSE,
6533     EL_SPACESHIP_UP,                    -1, -1
6534   },
6535   {
6536     Xtank_2_e,                          FALSE,  FALSE,
6537     EL_SPACESHIP_RIGHT,                 -1, -1
6538   },
6539   {
6540     Xtank_2_s,                          FALSE,  FALSE,
6541     EL_SPACESHIP_DOWN,                  -1, -1
6542   },
6543   {
6544     Xtank_2_w,                          FALSE,  FALSE,
6545     EL_SPACESHIP_LEFT,                  -1, -1
6546   },
6547   {
6548     Ytank_n,                            FALSE,  FALSE,
6549     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
6550   },
6551   {
6552     Ytank_nB,                           FALSE,  TRUE,
6553     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_UP
6554   },
6555   {
6556     Ytank_e,                            FALSE,  FALSE,
6557     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
6558   },
6559   {
6560     Ytank_eB,                           FALSE,  TRUE,
6561     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_RIGHT
6562   },
6563   {
6564     Ytank_s,                            FALSE,  FALSE,
6565     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
6566   },
6567   {
6568     Ytank_sB,                           FALSE,  TRUE,
6569     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_DOWN
6570   },
6571   {
6572     Ytank_w,                            FALSE,  FALSE,
6573     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
6574   },
6575   {
6576     Ytank_wB,                           FALSE,  TRUE,
6577     EL_SPACESHIP,                       ACTION_MOVING, MV_BIT_LEFT
6578   },
6579   {
6580     Ytank_w_n,                          FALSE,  FALSE,
6581     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6582   },
6583   {
6584     Ytank_n_e,                          FALSE,  FALSE,
6585     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6586   },
6587   {
6588     Ytank_e_s,                          FALSE,  FALSE,
6589     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6590   },
6591   {
6592     Ytank_s_w,                          FALSE,  FALSE,
6593     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6594   },
6595   {
6596     Ytank_e_n,                          FALSE,  FALSE,
6597     EL_SPACESHIP,                       ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6598   },
6599   {
6600     Ytank_s_e,                          FALSE,  FALSE,
6601     EL_SPACESHIP,                       ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6602   },
6603   {
6604     Ytank_w_s,                          FALSE,  FALSE,
6605     EL_SPACESHIP,                       ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6606   },
6607   {
6608     Ytank_n_w,                          FALSE,  FALSE,
6609     EL_SPACESHIP,                       ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6610   },
6611   {
6612     Ytank_stone,                        FALSE,  FALSE,
6613     EL_SPACESHIP,                       ACTION_SMASHED_BY_ROCK, -1
6614   },
6615   {
6616     Ytank_spring,                       FALSE,  FALSE,
6617     EL_SPACESHIP,                       ACTION_SMASHED_BY_SPRING, -1
6618   },
6619
6620   {
6621     Xemerald,                           TRUE,   FALSE,
6622     EL_EMERALD,                         -1, -1
6623   },
6624   {
6625     Xemerald_pause,                     FALSE,  FALSE,
6626     EL_EMERALD,                         -1, -1
6627   },
6628   {
6629     Xemerald_fall,                      FALSE,  FALSE,
6630     EL_EMERALD,                         -1, -1
6631   },
6632   {
6633     Xemerald_shine,                     FALSE,  FALSE,
6634     EL_EMERALD,                         ACTION_TWINKLING, -1
6635   },
6636   {
6637     Yemerald_s,                         FALSE,  FALSE,
6638     EL_EMERALD,                         ACTION_FALLING, -1
6639   },
6640   {
6641     Yemerald_sB,                        FALSE,  TRUE,
6642     EL_EMERALD,                         ACTION_FALLING, -1
6643   },
6644   {
6645     Yemerald_e,                         FALSE,  FALSE,
6646     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
6647   },
6648   {
6649     Yemerald_eB,                        FALSE,  TRUE,
6650     EL_EMERALD,                         ACTION_MOVING, MV_BIT_RIGHT
6651   },
6652   {
6653     Yemerald_w,                         FALSE,  FALSE,
6654     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
6655   },
6656   {
6657     Yemerald_wB,                        FALSE,  TRUE,
6658     EL_EMERALD,                         ACTION_MOVING, MV_BIT_LEFT
6659   },
6660   {
6661     Yemerald_blank,                     FALSE,  FALSE,
6662     EL_EMERALD,                         ACTION_COLLECTING, -1
6663   },
6664
6665   {
6666     Xdiamond,                           TRUE,   FALSE,
6667     EL_DIAMOND,                         -1, -1
6668   },
6669   {
6670     Xdiamond_pause,                     FALSE,  FALSE,
6671     EL_DIAMOND,                         -1, -1
6672   },
6673   {
6674     Xdiamond_fall,                      FALSE,  FALSE,
6675     EL_DIAMOND,                         -1, -1
6676   },
6677   {
6678     Xdiamond_shine,                     FALSE,  FALSE,
6679     EL_DIAMOND,                         ACTION_TWINKLING, -1
6680   },
6681   {
6682     Ydiamond_s,                         FALSE,  FALSE,
6683     EL_DIAMOND,                         ACTION_FALLING, -1
6684   },
6685   {
6686     Ydiamond_sB,                        FALSE,  TRUE,
6687     EL_DIAMOND,                         ACTION_FALLING, -1
6688   },
6689   {
6690     Ydiamond_e,                         FALSE,  FALSE,
6691     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
6692   },
6693   {
6694     Ydiamond_eB,                        FALSE,  TRUE,
6695     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_RIGHT
6696   },
6697   {
6698     Ydiamond_w,                         FALSE,  FALSE,
6699     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
6700   },
6701   {
6702     Ydiamond_wB,                        FALSE,  TRUE,
6703     EL_DIAMOND,                         ACTION_MOVING, MV_BIT_LEFT
6704   },
6705   {
6706     Ydiamond_blank,                     FALSE,  FALSE,
6707     EL_DIAMOND,                         ACTION_COLLECTING, -1
6708   },
6709   {
6710     Ydiamond_stone,                     FALSE,  FALSE,
6711     EL_DIAMOND,                         ACTION_SMASHED_BY_ROCK, -1
6712   },
6713
6714   {
6715     Xstone,                             TRUE,   FALSE,
6716     EL_ROCK,                            -1, -1
6717   },
6718   {
6719     Xstone_pause,                       FALSE,  FALSE,
6720     EL_ROCK,                            -1, -1
6721   },
6722   {
6723     Xstone_fall,                        FALSE,  FALSE,
6724     EL_ROCK,                            -1, -1
6725   },
6726   {
6727     Ystone_s,                           FALSE,  FALSE,
6728     EL_ROCK,                            ACTION_FALLING, -1
6729   },
6730   {
6731     Ystone_sB,                          FALSE,  TRUE,
6732     EL_ROCK,                            ACTION_FALLING, -1
6733   },
6734   {
6735     Ystone_e,                           FALSE,  FALSE,
6736     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
6737   },
6738   {
6739     Ystone_eB,                          FALSE,  TRUE,
6740     EL_ROCK,                            ACTION_MOVING, MV_BIT_RIGHT
6741   },
6742   {
6743     Ystone_w,                           FALSE,  FALSE,
6744     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
6745   },
6746   {
6747     Ystone_wB,                          FALSE,  TRUE,
6748     EL_ROCK,                            ACTION_MOVING, MV_BIT_LEFT
6749   },
6750
6751   {
6752     Xbomb,                              TRUE,   FALSE,
6753     EL_BOMB,                            -1, -1
6754   },
6755   {
6756     Xbomb_pause,                        FALSE,  FALSE,
6757     EL_BOMB,                            -1, -1
6758   },
6759   {
6760     Xbomb_fall,                         FALSE,  FALSE,
6761     EL_BOMB,                            -1, -1
6762   },
6763   {
6764     Ybomb_s,                            FALSE,  FALSE,
6765     EL_BOMB,                            ACTION_FALLING, -1
6766   },
6767   {
6768     Ybomb_sB,                           FALSE,  TRUE,
6769     EL_BOMB,                            ACTION_FALLING, -1
6770   },
6771   {
6772     Ybomb_e,                            FALSE,  FALSE,
6773     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
6774   },
6775   {
6776     Ybomb_eB,                           FALSE,  TRUE,
6777     EL_BOMB,                            ACTION_MOVING, MV_BIT_RIGHT
6778   },
6779   {
6780     Ybomb_w,                            FALSE,  FALSE,
6781     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
6782   },
6783   {
6784     Ybomb_wB,                           FALSE,  TRUE,
6785     EL_BOMB,                            ACTION_MOVING, MV_BIT_LEFT
6786   },
6787   {
6788     Ybomb_blank,                        FALSE,  FALSE,
6789     EL_BOMB,                            ACTION_ACTIVATING, -1
6790   },
6791
6792   {
6793     Xnut,                               TRUE,   FALSE,
6794     EL_NUT,                             -1, -1
6795   },
6796   {
6797     Xnut_pause,                         FALSE,  FALSE,
6798     EL_NUT,                             -1, -1
6799   },
6800   {
6801     Xnut_fall,                          FALSE,  FALSE,
6802     EL_NUT,                             -1, -1
6803   },
6804   {
6805     Ynut_s,                             FALSE,  FALSE,
6806     EL_NUT,                             ACTION_FALLING, -1
6807   },
6808   {
6809     Ynut_sB,                            FALSE,  TRUE,
6810     EL_NUT,                             ACTION_FALLING, -1
6811   },
6812   {
6813     Ynut_e,                             FALSE,  FALSE,
6814     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
6815   },
6816   {
6817     Ynut_eB,                            FALSE,  TRUE,
6818     EL_NUT,                             ACTION_MOVING, MV_BIT_RIGHT
6819   },
6820   {
6821     Ynut_w,                             FALSE,  FALSE,
6822     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
6823   },
6824   {
6825     Ynut_wB,                            FALSE,  TRUE,
6826     EL_NUT,                             ACTION_MOVING, MV_BIT_LEFT
6827   },
6828   {
6829     Ynut_stone,                         FALSE,  FALSE,
6830     EL_NUT,                             ACTION_BREAKING, -1
6831   },
6832
6833   {
6834     Xspring,                            TRUE,   FALSE,
6835     EL_SPRING,                          -1, -1
6836   },
6837   {
6838     Xspring_pause,                      FALSE,  FALSE,
6839     EL_SPRING,                          -1, -1
6840   },
6841   {
6842     Xspring_e,                          TRUE,   FALSE,
6843     EL_SPRING_RIGHT,                    -1, -1
6844   },
6845   {
6846     Xspring_w,                          TRUE,   FALSE,
6847     EL_SPRING_LEFT,                     -1, -1
6848   },
6849   {
6850     Xspring_fall,                       FALSE,  FALSE,
6851     EL_SPRING,                          -1, -1
6852   },
6853   {
6854     Yspring_s,                          FALSE,  FALSE,
6855     EL_SPRING,                          ACTION_FALLING, -1
6856   },
6857   {
6858     Yspring_sB,                         FALSE,  TRUE,
6859     EL_SPRING,                          ACTION_FALLING, -1
6860   },
6861   {
6862     Yspring_e,                          FALSE,  FALSE,
6863     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
6864   },
6865   {
6866     Yspring_eB,                         FALSE,  TRUE,
6867     EL_SPRING,                          ACTION_MOVING, MV_BIT_RIGHT
6868   },
6869   {
6870     Yspring_w,                          FALSE,  FALSE,
6871     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
6872   },
6873   {
6874     Yspring_wB,                         FALSE,  TRUE,
6875     EL_SPRING,                          ACTION_MOVING, MV_BIT_LEFT
6876   },
6877   {
6878     Yspring_alien_e,                    FALSE,  FALSE,
6879     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
6880   },
6881   {
6882     Yspring_alien_eB,                   FALSE,  TRUE,
6883     EL_SPRING,                          ACTION_EATING, MV_BIT_RIGHT
6884   },
6885   {
6886     Yspring_alien_w,                    FALSE,  FALSE,
6887     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
6888   },
6889   {
6890     Yspring_alien_wB,                   FALSE,  TRUE,
6891     EL_SPRING,                          ACTION_EATING, MV_BIT_LEFT
6892   },
6893
6894   {
6895     Xpush_emerald_e,                    FALSE,  FALSE,
6896     EL_EMERALD,                         -1, MV_BIT_RIGHT
6897   },
6898   {
6899     Xpush_emerald_w,                    FALSE,  FALSE,
6900     EL_EMERALD,                         -1, MV_BIT_LEFT
6901   },
6902   {
6903     Xpush_diamond_e,                    FALSE,  FALSE,
6904     EL_DIAMOND,                         -1, MV_BIT_RIGHT
6905   },
6906   {
6907     Xpush_diamond_w,                    FALSE,  FALSE,
6908     EL_DIAMOND,                         -1, MV_BIT_LEFT
6909   },
6910   {
6911     Xpush_stone_e,                      FALSE,  FALSE,
6912     EL_ROCK,                            -1, MV_BIT_RIGHT
6913   },
6914   {
6915     Xpush_stone_w,                      FALSE,  FALSE,
6916     EL_ROCK,                            -1, MV_BIT_LEFT
6917   },
6918   {
6919     Xpush_bomb_e,                       FALSE,  FALSE,
6920     EL_BOMB,                            -1, MV_BIT_RIGHT
6921   },
6922   {
6923     Xpush_bomb_w,                       FALSE,  FALSE,
6924     EL_BOMB,                            -1, MV_BIT_LEFT
6925   },
6926   {
6927     Xpush_nut_e,                        FALSE,  FALSE,
6928     EL_NUT,                             -1, MV_BIT_RIGHT
6929   },
6930   {
6931     Xpush_nut_w,                        FALSE,  FALSE,
6932     EL_NUT,                             -1, MV_BIT_LEFT
6933   },
6934   {
6935     Xpush_spring_e,                     FALSE,  FALSE,
6936     EL_SPRING_RIGHT,                    -1, MV_BIT_RIGHT
6937   },
6938   {
6939     Xpush_spring_w,                     FALSE,  FALSE,
6940     EL_SPRING_LEFT,                     -1, MV_BIT_LEFT
6941   },
6942
6943   {
6944     Xdynamite,                          TRUE,   FALSE,
6945     EL_EM_DYNAMITE,                     -1, -1
6946   },
6947   {
6948     Ydynamite_blank,                    FALSE,  FALSE,
6949     EL_EM_DYNAMITE,                     ACTION_COLLECTING, -1
6950   },
6951   {
6952     Xdynamite_1,                        TRUE,   FALSE,
6953     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6954   },
6955   {
6956     Xdynamite_2,                        FALSE,  FALSE,
6957     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6958   },
6959   {
6960     Xdynamite_3,                        FALSE,  FALSE,
6961     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6962   },
6963   {
6964     Xdynamite_4,                        FALSE,  FALSE,
6965     EL_EM_DYNAMITE_ACTIVE,              -1, -1
6966   },
6967
6968   {
6969     Xkey_1,                             TRUE,   FALSE,
6970     EL_EM_KEY_1,                        -1, -1
6971   },
6972   {
6973     Xkey_2,                             TRUE,   FALSE,
6974     EL_EM_KEY_2,                        -1, -1
6975   },
6976   {
6977     Xkey_3,                             TRUE,   FALSE,
6978     EL_EM_KEY_3,                        -1, -1
6979   },
6980   {
6981     Xkey_4,                             TRUE,   FALSE,
6982     EL_EM_KEY_4,                        -1, -1
6983   },
6984   {
6985     Xkey_5,                             TRUE,   FALSE,
6986     EL_EMC_KEY_5,                       -1, -1
6987   },
6988   {
6989     Xkey_6,                             TRUE,   FALSE,
6990     EL_EMC_KEY_6,                       -1, -1
6991   },
6992   {
6993     Xkey_7,                             TRUE,   FALSE,
6994     EL_EMC_KEY_7,                       -1, -1
6995   },
6996   {
6997     Xkey_8,                             TRUE,   FALSE,
6998     EL_EMC_KEY_8,                       -1, -1
6999   },
7000
7001   {
7002     Xdoor_1,                            TRUE,   FALSE,
7003     EL_EM_GATE_1,                       -1, -1
7004   },
7005   {
7006     Xdoor_2,                            TRUE,   FALSE,
7007     EL_EM_GATE_2,                       -1, -1
7008   },
7009   {
7010     Xdoor_3,                            TRUE,   FALSE,
7011     EL_EM_GATE_3,                       -1, -1
7012   },
7013   {
7014     Xdoor_4,                            TRUE,   FALSE,
7015     EL_EM_GATE_4,                       -1, -1
7016   },
7017   {
7018     Xdoor_5,                            TRUE,   FALSE,
7019     EL_EMC_GATE_5,                      -1, -1
7020   },
7021   {
7022     Xdoor_6,                            TRUE,   FALSE,
7023     EL_EMC_GATE_6,                      -1, -1
7024   },
7025   {
7026     Xdoor_7,                            TRUE,   FALSE,
7027     EL_EMC_GATE_7,                      -1, -1
7028   },
7029   {
7030     Xdoor_8,                            TRUE,   FALSE,
7031     EL_EMC_GATE_8,                      -1, -1
7032   },
7033
7034   {
7035     Xfake_door_1,                       TRUE,   FALSE,
7036     EL_EM_GATE_1_GRAY,                  -1, -1
7037   },
7038   {
7039     Xfake_door_2,                       TRUE,   FALSE,
7040     EL_EM_GATE_2_GRAY,                  -1, -1
7041   },
7042   {
7043     Xfake_door_3,                       TRUE,   FALSE,
7044     EL_EM_GATE_3_GRAY,                  -1, -1
7045   },
7046   {
7047     Xfake_door_4,                       TRUE,   FALSE,
7048     EL_EM_GATE_4_GRAY,                  -1, -1
7049   },
7050   {
7051     Xfake_door_5,                       TRUE,   FALSE,
7052     EL_EMC_GATE_5_GRAY,                 -1, -1
7053   },
7054   {
7055     Xfake_door_6,                       TRUE,   FALSE,
7056     EL_EMC_GATE_6_GRAY,                 -1, -1
7057   },
7058   {
7059     Xfake_door_7,                       TRUE,   FALSE,
7060     EL_EMC_GATE_7_GRAY,                 -1, -1
7061   },
7062   {
7063     Xfake_door_8,                       TRUE,   FALSE,
7064     EL_EMC_GATE_8_GRAY,                 -1, -1
7065   },
7066
7067   {
7068     Xballoon,                           TRUE,   FALSE,
7069     EL_BALLOON,                         -1, -1
7070   },
7071   {
7072     Yballoon_n,                         FALSE,  FALSE,
7073     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
7074   },
7075   {
7076     Yballoon_nB,                        FALSE,  TRUE,
7077     EL_BALLOON,                         ACTION_MOVING, MV_BIT_UP
7078   },
7079   {
7080     Yballoon_e,                         FALSE,  FALSE,
7081     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
7082   },
7083   {
7084     Yballoon_eB,                        FALSE,  TRUE,
7085     EL_BALLOON,                         ACTION_MOVING, MV_BIT_RIGHT
7086   },
7087   {
7088     Yballoon_s,                         FALSE,  FALSE,
7089     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
7090   },
7091   {
7092     Yballoon_sB,                        FALSE,  TRUE,
7093     EL_BALLOON,                         ACTION_MOVING, MV_BIT_DOWN
7094   },
7095   {
7096     Yballoon_w,                         FALSE,  FALSE,
7097     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
7098   },
7099   {
7100     Yballoon_wB,                        FALSE,  TRUE,
7101     EL_BALLOON,                         ACTION_MOVING, MV_BIT_LEFT
7102   },
7103
7104   {
7105     Xball_1,                            TRUE,   FALSE,
7106     EL_EMC_MAGIC_BALL,                  -1, -1
7107   },
7108   {
7109     Yball_1,                            FALSE,  FALSE,
7110     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
7111   },
7112   {
7113     Xball_2,                            FALSE,  FALSE,
7114     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
7115   },
7116   {
7117     Yball_2,                            FALSE,  FALSE,
7118     EL_EMC_MAGIC_BALL,                  ACTION_ACTIVE, -1
7119   },
7120   {
7121     Yball_blank,                        FALSE,  FALSE,
7122     EL_EMC_MAGIC_BALL,                  ACTION_DROPPING, -1
7123   },
7124
7125   {
7126     Xamoeba_1,                          TRUE,   FALSE,
7127     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
7128   },
7129   {
7130     Xamoeba_2,                          FALSE,  FALSE,
7131     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
7132   },
7133   {
7134     Xamoeba_3,                          FALSE,  FALSE,
7135     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
7136   },
7137   {
7138     Xamoeba_4,                          FALSE,  FALSE,
7139     EL_AMOEBA_DRY,                      ACTION_OTHER, -1
7140   },
7141   {
7142     Xamoeba_5,                          TRUE,   FALSE,
7143     EL_AMOEBA_WET,                      ACTION_OTHER, -1
7144   },
7145   {
7146     Xamoeba_6,                          FALSE,  FALSE,
7147     EL_AMOEBA_WET,                      ACTION_OTHER, -1
7148   },
7149   {
7150     Xamoeba_7,                          FALSE,  FALSE,
7151     EL_AMOEBA_WET,                      ACTION_OTHER, -1
7152   },
7153   {
7154     Xamoeba_8,                          FALSE,  FALSE,
7155     EL_AMOEBA_WET,                      ACTION_OTHER, -1
7156   },
7157
7158   {
7159     Xdrip,                              TRUE,   FALSE,
7160     EL_AMOEBA_DROP,                     ACTION_GROWING, -1
7161   },
7162   {
7163     Xdrip_fall,                         FALSE,  FALSE,
7164     EL_AMOEBA_DROP,                     -1, -1
7165   },
7166   {
7167     Xdrip_stretch,                      FALSE,  FALSE,
7168     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
7169   },
7170   {
7171     Xdrip_stretchB,                     FALSE,  TRUE,
7172     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
7173   },
7174   {
7175     Ydrip_1_s,                          FALSE,  FALSE,
7176     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
7177   },
7178   {
7179     Ydrip_1_sB,                         FALSE,  TRUE,
7180     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
7181   },
7182   {
7183     Ydrip_2_s,                          FALSE,  FALSE,
7184     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
7185   },
7186   {
7187     Ydrip_2_sB,                         FALSE,  TRUE,
7188     EL_AMOEBA_DROP,                     ACTION_FALLING, -1
7189   },
7190
7191   {
7192     Xwonderwall,                        TRUE,   FALSE,
7193     EL_MAGIC_WALL,                      -1, -1
7194   },
7195   {
7196     Ywonderwall,                        FALSE,  FALSE,
7197     EL_MAGIC_WALL,                      ACTION_ACTIVE, -1
7198   },
7199
7200   {
7201     Xwheel,                             TRUE,   FALSE,
7202     EL_ROBOT_WHEEL,                     -1, -1
7203   },
7204   {
7205     Ywheel,                             FALSE,  FALSE,
7206     EL_ROBOT_WHEEL,                     ACTION_ACTIVE, -1
7207   },
7208
7209   {
7210     Xswitch,                            TRUE,   FALSE,
7211     EL_EMC_MAGIC_BALL_SWITCH,           -1, -1
7212   },
7213   {
7214     Yswitch,                            FALSE,  FALSE,
7215     EL_EMC_MAGIC_BALL_SWITCH,           ACTION_ACTIVE, -1
7216   },
7217
7218   {
7219     Xbumper,                            TRUE,   FALSE,
7220     EL_EMC_SPRING_BUMPER,               -1, -1
7221   },
7222   {
7223     Ybumper,                            FALSE,  FALSE,
7224     EL_EMC_SPRING_BUMPER,               ACTION_ACTIVE, -1
7225   },
7226
7227   {
7228     Xacid_nw,                           TRUE,   FALSE,
7229     EL_ACID_POOL_TOPLEFT,               -1, -1
7230   },
7231   {
7232     Xacid_ne,                           TRUE,   FALSE,
7233     EL_ACID_POOL_TOPRIGHT,              -1, -1
7234   },
7235   {
7236     Xacid_sw,                           TRUE,   FALSE,
7237     EL_ACID_POOL_BOTTOMLEFT,            -1, -1
7238   },
7239   {
7240     Xacid_s,                            TRUE,   FALSE,
7241     EL_ACID_POOL_BOTTOM,                -1, -1
7242   },
7243   {
7244     Xacid_se,                           TRUE,   FALSE,
7245     EL_ACID_POOL_BOTTOMRIGHT,           -1, -1
7246   },
7247
7248   {
7249     Xfake_blank,                        TRUE,   FALSE,
7250     EL_INVISIBLE_WALL,                  -1, -1
7251   },
7252   {
7253     Yfake_blank,                        FALSE,  FALSE,
7254     EL_INVISIBLE_WALL,                  ACTION_ACTIVE, -1
7255   },
7256
7257   {
7258     Xfake_grass,                        TRUE,   FALSE,
7259     EL_EMC_FAKE_GRASS,                  -1, -1
7260   },
7261   {
7262     Yfake_grass,                        FALSE,  FALSE,
7263     EL_EMC_FAKE_GRASS,                  ACTION_ACTIVE, -1
7264   },
7265
7266   {
7267     Xfake_amoeba,                       TRUE,   FALSE,
7268     EL_EMC_DRIPPER,                     -1, -1
7269   },
7270   {
7271     Yfake_amoeba,                       FALSE,  FALSE,
7272     EL_EMC_DRIPPER,                     ACTION_ACTIVE, -1
7273   },
7274
7275   {
7276     Xlenses,                            TRUE,   FALSE,
7277     EL_EMC_LENSES,                      -1, -1
7278   },
7279
7280   {
7281     Xmagnify,                           TRUE,   FALSE,
7282     EL_EMC_MAGNIFIER,                   -1, -1
7283   },
7284
7285   {
7286     Xsand,                              TRUE,   FALSE,
7287     EL_QUICKSAND_EMPTY,                 -1, -1
7288   },
7289   {
7290     Xsand_stone,                        TRUE,   FALSE,
7291     EL_QUICKSAND_FULL,                  -1, -1
7292   },
7293   {
7294     Xsand_stonein_1,                    FALSE,  TRUE,
7295     EL_ROCK,                            ACTION_FILLING, -1
7296   },
7297   {
7298     Xsand_stonein_2,                    FALSE,  TRUE,
7299     EL_ROCK,                            ACTION_FILLING, -1
7300   },
7301   {
7302     Xsand_stonein_3,                    FALSE,  TRUE,
7303     EL_ROCK,                            ACTION_FILLING, -1
7304   },
7305   {
7306     Xsand_stonein_4,                    FALSE,  TRUE,
7307     EL_ROCK,                            ACTION_FILLING, -1
7308   },
7309   {
7310     Xsand_sandstone_1,                  FALSE,  FALSE,
7311     EL_QUICKSAND_FILLING,               -1, -1
7312   },
7313   {
7314     Xsand_sandstone_2,                  FALSE,  FALSE,
7315     EL_QUICKSAND_FILLING,               -1, -1
7316   },
7317   {
7318     Xsand_sandstone_3,                  FALSE,  FALSE,
7319     EL_QUICKSAND_FILLING,               -1, -1
7320   },
7321   {
7322     Xsand_sandstone_4,                  FALSE,  FALSE,
7323     EL_QUICKSAND_FILLING,               -1, -1
7324   },
7325   {
7326     Xsand_stonesand_1,                  FALSE,  FALSE,
7327     EL_QUICKSAND_EMPTYING,              -1, -1
7328   },
7329   {
7330     Xsand_stonesand_2,                  FALSE,  FALSE,
7331     EL_QUICKSAND_EMPTYING,              -1, -1
7332   },
7333   {
7334     Xsand_stonesand_3,                  FALSE,  FALSE,
7335     EL_QUICKSAND_EMPTYING,              -1, -1
7336   },
7337   {
7338     Xsand_stonesand_4,                  FALSE,  FALSE,
7339     EL_QUICKSAND_EMPTYING,              -1, -1
7340   },
7341   {
7342     Xsand_stoneout_1,                   FALSE,  FALSE,
7343     EL_ROCK,                            ACTION_EMPTYING, -1
7344   },
7345   {
7346     Xsand_stoneout_2,                   FALSE,  FALSE,
7347     EL_ROCK,                            ACTION_EMPTYING, -1
7348   },
7349   {
7350     Xsand_stonesand_quickout_1,         FALSE,  FALSE,
7351     EL_QUICKSAND_EMPTYING,              -1, -1
7352   },
7353   {
7354     Xsand_stonesand_quickout_2,         FALSE,  FALSE,
7355     EL_QUICKSAND_EMPTYING,              -1, -1
7356   },
7357
7358   {
7359     Xslide_ns,                          TRUE,   FALSE,
7360     EL_EXPANDABLE_WALL_VERTICAL,        -1, -1
7361   },
7362   {
7363     Yslide_ns_blank,                    FALSE,  FALSE,
7364     EL_EXPANDABLE_WALL_VERTICAL,        ACTION_GROWING, -1
7365   },
7366   {
7367     Xslide_ew,                          TRUE,   FALSE,
7368     EL_EXPANDABLE_WALL_HORIZONTAL,      -1, -1
7369   },
7370   {
7371     Yslide_ew_blank,                    FALSE,  FALSE,
7372     EL_EXPANDABLE_WALL_HORIZONTAL,      ACTION_GROWING, -1
7373   },
7374
7375   {
7376     Xwind_n,                            TRUE,   FALSE,
7377     EL_BALLOON_SWITCH_UP,               -1, -1
7378   },
7379   {
7380     Xwind_e,                            TRUE,   FALSE,
7381     EL_BALLOON_SWITCH_RIGHT,            -1, -1
7382   },
7383   {
7384     Xwind_s,                            TRUE,   FALSE,
7385     EL_BALLOON_SWITCH_DOWN,             -1, -1
7386   },
7387   {
7388     Xwind_w,                            TRUE,   FALSE,
7389     EL_BALLOON_SWITCH_LEFT,             -1, -1
7390   },
7391   {
7392     Xwind_any,                          TRUE,   FALSE,
7393     EL_BALLOON_SWITCH_ANY,              -1, -1
7394   },
7395   {
7396     Xwind_stop,                         TRUE,   FALSE,
7397     EL_BALLOON_SWITCH_NONE,             -1, -1
7398   },
7399
7400   {
7401     Xexit,                              TRUE,   FALSE,
7402     EL_EM_EXIT_CLOSED,                  -1, -1
7403   },
7404   {
7405     Xexit_1,                            TRUE,   FALSE,
7406     EL_EM_EXIT_OPEN,                    -1, -1
7407   },
7408   {
7409     Xexit_2,                            FALSE,  FALSE,
7410     EL_EM_EXIT_OPEN,                    -1, -1
7411   },
7412   {
7413     Xexit_3,                            FALSE,  FALSE,
7414     EL_EM_EXIT_OPEN,                    -1, -1
7415   },
7416
7417   {
7418     Xpause,                             FALSE,  FALSE,
7419     EL_EMPTY,                           -1, -1
7420   },
7421
7422   {
7423     Xwall_1,                            TRUE,   FALSE,
7424     EL_WALL,                            -1, -1
7425   },
7426   {
7427     Xwall_2,                            TRUE,   FALSE,
7428     EL_EMC_WALL_14,                     -1, -1
7429   },
7430   {
7431     Xwall_3,                            TRUE,   FALSE,
7432     EL_EMC_WALL_15,                     -1, -1
7433   },
7434   {
7435     Xwall_4,                            TRUE,   FALSE,
7436     EL_EMC_WALL_16,                     -1, -1
7437   },
7438
7439   {
7440     Xroundwall_1,                       TRUE,   FALSE,
7441     EL_WALL_SLIPPERY,                   -1, -1
7442   },
7443   {
7444     Xroundwall_2,                       TRUE,   FALSE,
7445     EL_EMC_WALL_SLIPPERY_2,             -1, -1
7446   },
7447   {
7448     Xroundwall_3,                       TRUE,   FALSE,
7449     EL_EMC_WALL_SLIPPERY_3,             -1, -1
7450   },
7451   {
7452     Xroundwall_4,                       TRUE,   FALSE,
7453     EL_EMC_WALL_SLIPPERY_4,             -1, -1
7454   },
7455
7456   {
7457     Xsteel_1,                           TRUE,   FALSE,
7458     EL_STEELWALL,                       -1, -1
7459   },
7460   {
7461     Xsteel_2,                           TRUE,   FALSE,
7462     EL_EMC_STEELWALL_2,                 -1, -1
7463   },
7464   {
7465     Xsteel_3,                           TRUE,   FALSE,
7466     EL_EMC_STEELWALL_3,                 -1, -1
7467   },
7468   {
7469     Xsteel_4,                           TRUE,   FALSE,
7470     EL_EMC_STEELWALL_4,                 -1, -1
7471   },
7472
7473   {
7474     Xdecor_1,                           TRUE,   FALSE,
7475     EL_EMC_WALL_8,                      -1, -1
7476   },
7477   {
7478     Xdecor_2,                           TRUE,   FALSE,
7479     EL_EMC_WALL_6,                      -1, -1
7480   },
7481   {
7482     Xdecor_3,                           TRUE,   FALSE,
7483     EL_EMC_WALL_4,                      -1, -1
7484   },
7485   {
7486     Xdecor_4,                           TRUE,   FALSE,
7487     EL_EMC_WALL_7,                      -1, -1
7488   },
7489   {
7490     Xdecor_5,                           TRUE,   FALSE,
7491     EL_EMC_WALL_5,                      -1, -1
7492   },
7493   {
7494     Xdecor_6,                           TRUE,   FALSE,
7495     EL_EMC_WALL_9,                      -1, -1
7496   },
7497   {
7498     Xdecor_7,                           TRUE,   FALSE,
7499     EL_EMC_WALL_10,                     -1, -1
7500   },
7501   {
7502     Xdecor_8,                           TRUE,   FALSE,
7503     EL_EMC_WALL_1,                      -1, -1
7504   },
7505   {
7506     Xdecor_9,                           TRUE,   FALSE,
7507     EL_EMC_WALL_2,                      -1, -1
7508   },
7509   {
7510     Xdecor_10,                          TRUE,   FALSE,
7511     EL_EMC_WALL_3,                      -1, -1
7512   },
7513   {
7514     Xdecor_11,                          TRUE,   FALSE,
7515     EL_EMC_WALL_11,                     -1, -1
7516   },
7517   {
7518     Xdecor_12,                          TRUE,   FALSE,
7519     EL_EMC_WALL_12,                     -1, -1
7520   },
7521
7522   {
7523     Xalpha_0,                           TRUE,   FALSE,
7524     EL_CHAR('0'),                       -1, -1
7525   },
7526   {
7527     Xalpha_1,                           TRUE,   FALSE,
7528     EL_CHAR('1'),                       -1, -1
7529   },
7530   {
7531     Xalpha_2,                           TRUE,   FALSE,
7532     EL_CHAR('2'),                       -1, -1
7533   },
7534   {
7535     Xalpha_3,                           TRUE,   FALSE,
7536     EL_CHAR('3'),                       -1, -1
7537   },
7538   {
7539     Xalpha_4,                           TRUE,   FALSE,
7540     EL_CHAR('4'),                       -1, -1
7541   },
7542   {
7543     Xalpha_5,                           TRUE,   FALSE,
7544     EL_CHAR('5'),                       -1, -1
7545   },
7546   {
7547     Xalpha_6,                           TRUE,   FALSE,
7548     EL_CHAR('6'),                       -1, -1
7549   },
7550   {
7551     Xalpha_7,                           TRUE,   FALSE,
7552     EL_CHAR('7'),                       -1, -1
7553   },
7554   {
7555     Xalpha_8,                           TRUE,   FALSE,
7556     EL_CHAR('8'),                       -1, -1
7557   },
7558   {
7559     Xalpha_9,                           TRUE,   FALSE,
7560     EL_CHAR('9'),                       -1, -1
7561   },
7562   {
7563     Xalpha_excla,                       TRUE,   FALSE,
7564     EL_CHAR('!'),                       -1, -1
7565   },
7566   {
7567     Xalpha_apost,                       TRUE,   FALSE,
7568     EL_CHAR('\''),                      -1, -1
7569   },
7570   {
7571     Xalpha_comma,                       TRUE,   FALSE,
7572     EL_CHAR(','),                       -1, -1
7573   },
7574   {
7575     Xalpha_minus,                       TRUE,   FALSE,
7576     EL_CHAR('-'),                       -1, -1
7577   },
7578   {
7579     Xalpha_perio,                       TRUE,   FALSE,
7580     EL_CHAR('.'),                       -1, -1
7581   },
7582   {
7583     Xalpha_colon,                       TRUE,   FALSE,
7584     EL_CHAR(':'),                       -1, -1
7585   },
7586   {
7587     Xalpha_quest,                       TRUE,   FALSE,
7588     EL_CHAR('?'),                       -1, -1
7589   },
7590   {
7591     Xalpha_a,                           TRUE,   FALSE,
7592     EL_CHAR('A'),                       -1, -1
7593   },
7594   {
7595     Xalpha_b,                           TRUE,   FALSE,
7596     EL_CHAR('B'),                       -1, -1
7597   },
7598   {
7599     Xalpha_c,                           TRUE,   FALSE,
7600     EL_CHAR('C'),                       -1, -1
7601   },
7602   {
7603     Xalpha_d,                           TRUE,   FALSE,
7604     EL_CHAR('D'),                       -1, -1
7605   },
7606   {
7607     Xalpha_e,                           TRUE,   FALSE,
7608     EL_CHAR('E'),                       -1, -1
7609   },
7610   {
7611     Xalpha_f,                           TRUE,   FALSE,
7612     EL_CHAR('F'),                       -1, -1
7613   },
7614   {
7615     Xalpha_g,                           TRUE,   FALSE,
7616     EL_CHAR('G'),                       -1, -1
7617   },
7618   {
7619     Xalpha_h,                           TRUE,   FALSE,
7620     EL_CHAR('H'),                       -1, -1
7621   },
7622   {
7623     Xalpha_i,                           TRUE,   FALSE,
7624     EL_CHAR('I'),                       -1, -1
7625   },
7626   {
7627     Xalpha_j,                           TRUE,   FALSE,
7628     EL_CHAR('J'),                       -1, -1
7629   },
7630   {
7631     Xalpha_k,                           TRUE,   FALSE,
7632     EL_CHAR('K'),                       -1, -1
7633   },
7634   {
7635     Xalpha_l,                           TRUE,   FALSE,
7636     EL_CHAR('L'),                       -1, -1
7637   },
7638   {
7639     Xalpha_m,                           TRUE,   FALSE,
7640     EL_CHAR('M'),                       -1, -1
7641   },
7642   {
7643     Xalpha_n,                           TRUE,   FALSE,
7644     EL_CHAR('N'),                       -1, -1
7645   },
7646   {
7647     Xalpha_o,                           TRUE,   FALSE,
7648     EL_CHAR('O'),                       -1, -1
7649   },
7650   {
7651     Xalpha_p,                           TRUE,   FALSE,
7652     EL_CHAR('P'),                       -1, -1
7653   },
7654   {
7655     Xalpha_q,                           TRUE,   FALSE,
7656     EL_CHAR('Q'),                       -1, -1
7657   },
7658   {
7659     Xalpha_r,                           TRUE,   FALSE,
7660     EL_CHAR('R'),                       -1, -1
7661   },
7662   {
7663     Xalpha_s,                           TRUE,   FALSE,
7664     EL_CHAR('S'),                       -1, -1
7665   },
7666   {
7667     Xalpha_t,                           TRUE,   FALSE,
7668     EL_CHAR('T'),                       -1, -1
7669   },
7670   {
7671     Xalpha_u,                           TRUE,   FALSE,
7672     EL_CHAR('U'),                       -1, -1
7673   },
7674   {
7675     Xalpha_v,                           TRUE,   FALSE,
7676     EL_CHAR('V'),                       -1, -1
7677   },
7678   {
7679     Xalpha_w,                           TRUE,   FALSE,
7680     EL_CHAR('W'),                       -1, -1
7681   },
7682   {
7683     Xalpha_x,                           TRUE,   FALSE,
7684     EL_CHAR('X'),                       -1, -1
7685   },
7686   {
7687     Xalpha_y,                           TRUE,   FALSE,
7688     EL_CHAR('Y'),                       -1, -1
7689   },
7690   {
7691     Xalpha_z,                           TRUE,   FALSE,
7692     EL_CHAR('Z'),                       -1, -1
7693   },
7694   {
7695     Xalpha_arrow_e,                     TRUE,   FALSE,
7696     EL_CHAR('>'),                       -1, -1
7697   },
7698   {
7699     Xalpha_arrow_w,                     TRUE,   FALSE,
7700     EL_CHAR('<'),                       -1, -1
7701   },
7702   {
7703     Xalpha_copyr,                       TRUE,   FALSE,
7704     EL_CHAR(CHAR_BYTE_COPYRIGHT),       -1, -1
7705   },
7706
7707   {
7708     Ykey_1_blank,                       FALSE,  FALSE,
7709     EL_EM_KEY_1,                        ACTION_COLLECTING, -1
7710   },
7711   {
7712     Ykey_2_blank,                       FALSE,  FALSE,
7713     EL_EM_KEY_2,                        ACTION_COLLECTING, -1
7714   },
7715   {
7716     Ykey_3_blank,                       FALSE,  FALSE,
7717     EL_EM_KEY_3,                        ACTION_COLLECTING, -1
7718   },
7719   {
7720     Ykey_4_blank,                       FALSE,  FALSE,
7721     EL_EM_KEY_4,                        ACTION_COLLECTING, -1
7722   },
7723   {
7724     Ykey_5_blank,                       FALSE,  FALSE,
7725     EL_EMC_KEY_5,                       ACTION_COLLECTING, -1
7726   },
7727   {
7728     Ykey_6_blank,                       FALSE,  FALSE,
7729     EL_EMC_KEY_6,                       ACTION_COLLECTING, -1
7730   },
7731   {
7732     Ykey_7_blank,                       FALSE,  FALSE,
7733     EL_EMC_KEY_7,                       ACTION_COLLECTING, -1
7734   },
7735   {
7736     Ykey_8_blank,                       FALSE,  FALSE,
7737     EL_EMC_KEY_8,                       ACTION_COLLECTING, -1
7738   },
7739   {
7740     Ylenses_blank,                      FALSE,  FALSE,
7741     EL_EMC_LENSES,                      ACTION_COLLECTING, -1
7742   },
7743   {
7744     Ymagnify_blank,                     FALSE,  FALSE,
7745     EL_EMC_MAGNIFIER,                   ACTION_COLLECTING, -1
7746   },
7747   {
7748     Ygrass_blank,                       FALSE,  FALSE,
7749     EL_EMC_GRASS,                       ACTION_SNAPPING, -1
7750   },
7751   {
7752     Ydirt_blank,                        FALSE,  FALSE,
7753     EL_SAND,                            ACTION_SNAPPING, -1
7754   },
7755
7756   {
7757     -1,                                 FALSE,  FALSE,
7758     -1,                                 -1, -1
7759   }
7760 };
7761
7762 static struct Mapping_EM_to_RND_player
7763 {
7764   int action_em;
7765   int player_nr;
7766
7767   int element_rnd;
7768   int action;
7769   int direction;
7770 }
7771 em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
7772 {
7773   {
7774     PLY_walk_n,                         0,
7775     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_UP,
7776   },
7777   {
7778     PLY_walk_e,                         0,
7779     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_RIGHT,
7780   },
7781   {
7782     PLY_walk_s,                         0,
7783     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_DOWN,
7784   },
7785   {
7786     PLY_walk_w,                         0,
7787     EL_PLAYER_1,                        ACTION_MOVING, MV_BIT_LEFT,
7788   },
7789   {
7790     PLY_push_n,                         0,
7791     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_UP,
7792   },
7793   {
7794     PLY_push_e,                         0,
7795     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_RIGHT,
7796   },
7797   {
7798     PLY_push_s,                         0,
7799     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_DOWN,
7800   },
7801   {
7802     PLY_push_w,                         0,
7803     EL_PLAYER_1,                        ACTION_PUSHING, MV_BIT_LEFT,
7804   },
7805   {
7806     PLY_shoot_n,                        0,
7807     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_UP,
7808   },
7809   {
7810     PLY_shoot_e,                        0,
7811     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7812   },
7813   {
7814     PLY_shoot_s,                        0,
7815     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_DOWN,
7816   },
7817   {
7818     PLY_shoot_w,                        0,
7819     EL_PLAYER_1,                        ACTION_SNAPPING, MV_BIT_LEFT,
7820   },
7821   {
7822     PLY_walk_n,                         1,
7823     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_UP,
7824   },
7825   {
7826     PLY_walk_e,                         1,
7827     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_RIGHT,
7828   },
7829   {
7830     PLY_walk_s,                         1,
7831     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_DOWN,
7832   },
7833   {
7834     PLY_walk_w,                         1,
7835     EL_PLAYER_2,                        ACTION_MOVING, MV_BIT_LEFT,
7836   },
7837   {
7838     PLY_push_n,                         1,
7839     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_UP,
7840   },
7841   {
7842     PLY_push_e,                         1,
7843     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_RIGHT,
7844   },
7845   {
7846     PLY_push_s,                         1,
7847     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_DOWN,
7848   },
7849   {
7850     PLY_push_w,                         1,
7851     EL_PLAYER_2,                        ACTION_PUSHING, MV_BIT_LEFT,
7852   },
7853   {
7854     PLY_shoot_n,                        1,
7855     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_UP,
7856   },
7857   {
7858     PLY_shoot_e,                        1,
7859     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7860   },
7861   {
7862     PLY_shoot_s,                        1,
7863     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_DOWN,
7864   },
7865   {
7866     PLY_shoot_w,                        1,
7867     EL_PLAYER_2,                        ACTION_SNAPPING, MV_BIT_LEFT,
7868   },
7869   {
7870     PLY_still,                          0,
7871     EL_PLAYER_1,                        ACTION_DEFAULT, -1,
7872   },
7873   {
7874     PLY_still,                          1,
7875     EL_PLAYER_2,                        ACTION_DEFAULT, -1,
7876   },
7877   {
7878     PLY_walk_n,                         2,
7879     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_UP,
7880   },
7881   {
7882     PLY_walk_e,                         2,
7883     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_RIGHT,
7884   },
7885   {
7886     PLY_walk_s,                         2,
7887     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_DOWN,
7888   },
7889   {
7890     PLY_walk_w,                         2,
7891     EL_PLAYER_3,                        ACTION_MOVING, MV_BIT_LEFT,
7892   },
7893   {
7894     PLY_push_n,                         2,
7895     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_UP,
7896   },
7897   {
7898     PLY_push_e,                         2,
7899     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_RIGHT,
7900   },
7901   {
7902     PLY_push_s,                         2,
7903     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_DOWN,
7904   },
7905   {
7906     PLY_push_w,                         2,
7907     EL_PLAYER_3,                        ACTION_PUSHING, MV_BIT_LEFT,
7908   },
7909   {
7910     PLY_shoot_n,                        2,
7911     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_UP,
7912   },
7913   {
7914     PLY_shoot_e,                        2,
7915     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7916   },
7917   {
7918     PLY_shoot_s,                        2,
7919     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_DOWN,
7920   },
7921   {
7922     PLY_shoot_w,                        2,
7923     EL_PLAYER_3,                        ACTION_SNAPPING, MV_BIT_LEFT,
7924   },
7925   {
7926     PLY_walk_n,                         3,
7927     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_UP,
7928   },
7929   {
7930     PLY_walk_e,                         3,
7931     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_RIGHT,
7932   },
7933   {
7934     PLY_walk_s,                         3,
7935     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_DOWN,
7936   },
7937   {
7938     PLY_walk_w,                         3,
7939     EL_PLAYER_4,                        ACTION_MOVING, MV_BIT_LEFT,
7940   },
7941   {
7942     PLY_push_n,                         3,
7943     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_UP,
7944   },
7945   {
7946     PLY_push_e,                         3,
7947     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_RIGHT,
7948   },
7949   {
7950     PLY_push_s,                         3,
7951     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_DOWN,
7952   },
7953   {
7954     PLY_push_w,                         3,
7955     EL_PLAYER_4,                        ACTION_PUSHING, MV_BIT_LEFT,
7956   },
7957   {
7958     PLY_shoot_n,                        3,
7959     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_UP,
7960   },
7961   {
7962     PLY_shoot_e,                        3,
7963     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_RIGHT,
7964   },
7965   {
7966     PLY_shoot_s,                        3,
7967     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_DOWN,
7968   },
7969   {
7970     PLY_shoot_w,                        3,
7971     EL_PLAYER_4,                        ACTION_SNAPPING, MV_BIT_LEFT,
7972   },
7973   {
7974     PLY_still,                          2,
7975     EL_PLAYER_3,                        ACTION_DEFAULT, -1,
7976   },
7977   {
7978     PLY_still,                          3,
7979     EL_PLAYER_4,                        ACTION_DEFAULT, -1,
7980   },
7981
7982   {
7983     -1,                                 -1,
7984     -1,                                 -1, -1
7985   }
7986 };
7987
7988 int map_element_RND_to_EM_cave(int element_rnd)
7989 {
7990   static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7991   static boolean mapping_initialized = FALSE;
7992
7993   if (!mapping_initialized)
7994   {
7995     int i;
7996
7997     // return "Xalpha_quest" for all undefined elements in mapping array
7998     for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7999       mapping_RND_to_EM[i] = Xalpha_quest;
8000
8001     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8002       if (em_object_mapping_list[i].is_rnd_to_em_mapping)
8003         mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
8004           em_object_mapping_list[i].element_em;
8005
8006     mapping_initialized = TRUE;
8007   }
8008
8009   if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
8010   {
8011     Warn("invalid RND level element %d", element_rnd);
8012
8013     return EL_UNKNOWN;
8014   }
8015
8016   return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
8017 }
8018
8019 int map_element_EM_to_RND_cave(int element_em_cave)
8020 {
8021   static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8022   static boolean mapping_initialized = FALSE;
8023
8024   if (!mapping_initialized)
8025   {
8026     int i;
8027
8028     // return "EL_UNKNOWN" for all undefined elements in mapping array
8029     for (i = 0; i < GAME_TILE_MAX; i++)
8030       mapping_EM_to_RND[i] = EL_UNKNOWN;
8031
8032     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8033       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8034         em_object_mapping_list[i].element_rnd;
8035
8036     mapping_initialized = TRUE;
8037   }
8038
8039   if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
8040   {
8041     Warn("invalid EM cave element %d", element_em_cave);
8042
8043     return EL_UNKNOWN;
8044   }
8045
8046   return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
8047 }
8048
8049 int map_element_EM_to_RND_game(int element_em_game)
8050 {
8051   static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
8052   static boolean mapping_initialized = FALSE;
8053
8054   if (!mapping_initialized)
8055   {
8056     int i;
8057
8058     // return "EL_UNKNOWN" for all undefined elements in mapping array
8059     for (i = 0; i < GAME_TILE_MAX; i++)
8060       mapping_EM_to_RND[i] = EL_UNKNOWN;
8061
8062     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8063       mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
8064         em_object_mapping_list[i].element_rnd;
8065
8066     mapping_initialized = TRUE;
8067   }
8068
8069   if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
8070   {
8071     Warn("invalid EM game element %d", element_em_game);
8072
8073     return EL_UNKNOWN;
8074   }
8075
8076   return mapping_EM_to_RND[element_em_game];
8077 }
8078
8079 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
8080 {
8081   struct LevelInfo_EM *level_em = level->native_em_level;
8082   struct CAVE *cav = level_em->cav;
8083   int i, j;
8084
8085   for (i = 0; i < GAME_TILE_MAX; i++)
8086     cav->android_array[i] = Cblank;
8087
8088   for (i = 0; i < level->num_android_clone_elements; i++)
8089   {
8090     int element_rnd = level->android_clone_element[i];
8091     int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
8092
8093     for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
8094       if (em_object_mapping_list[j].element_rnd == element_rnd)
8095         cav->android_array[em_object_mapping_list[j].element_em] =
8096           element_em_cave;
8097   }
8098 }
8099
8100 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
8101 {
8102   struct LevelInfo_EM *level_em = level->native_em_level;
8103   struct CAVE *cav = level_em->cav;
8104   int i, j;
8105
8106   level->num_android_clone_elements = 0;
8107
8108   for (i = 0; i < GAME_TILE_MAX; i++)
8109   {
8110     int element_em_cave = cav->android_array[i];
8111     int element_rnd;
8112     boolean element_found = FALSE;
8113
8114     if (element_em_cave == Cblank)
8115       continue;
8116
8117     element_rnd = map_element_EM_to_RND_cave(element_em_cave);
8118
8119     for (j = 0; j < level->num_android_clone_elements; j++)
8120       if (level->android_clone_element[j] == element_rnd)
8121         element_found = TRUE;
8122
8123     if (!element_found)
8124     {
8125       level->android_clone_element[level->num_android_clone_elements++] =
8126         element_rnd;
8127
8128       if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
8129         break;
8130     }
8131   }
8132
8133   if (level->num_android_clone_elements == 0)
8134   {
8135     level->num_android_clone_elements = 1;
8136     level->android_clone_element[0] = EL_EMPTY;
8137   }
8138 }
8139
8140 int map_direction_RND_to_EM(int direction)
8141 {
8142   return (direction == MV_UP    ? 0 :
8143           direction == MV_RIGHT ? 1 :
8144           direction == MV_DOWN  ? 2 :
8145           direction == MV_LEFT  ? 3 :
8146           -1);
8147 }
8148
8149 int map_direction_EM_to_RND(int direction)
8150 {
8151   return (direction == 0 ? MV_UP    :
8152           direction == 1 ? MV_RIGHT :
8153           direction == 2 ? MV_DOWN  :
8154           direction == 3 ? MV_LEFT  :
8155           MV_NONE);
8156 }
8157
8158 int map_element_RND_to_SP(int element_rnd)
8159 {
8160   int element_sp = 0x20;        // map unknown elements to yellow "hardware"
8161
8162   if (element_rnd >= EL_SP_START &&
8163       element_rnd <= EL_SP_END)
8164     element_sp = element_rnd - EL_SP_START;
8165   else if (element_rnd == EL_EMPTY_SPACE)
8166     element_sp = 0x00;
8167   else if (element_rnd == EL_INVISIBLE_WALL)
8168     element_sp = 0x28;
8169
8170   return element_sp;
8171 }
8172
8173 int map_element_SP_to_RND(int element_sp)
8174 {
8175   int element_rnd = EL_UNKNOWN;
8176
8177   if (element_sp >= 0x00 &&
8178       element_sp <= 0x27)
8179     element_rnd = EL_SP_START + element_sp;
8180   else if (element_sp == 0x28)
8181     element_rnd = EL_INVISIBLE_WALL;
8182
8183   return element_rnd;
8184 }
8185
8186 int map_action_SP_to_RND(int action_sp)
8187 {
8188   switch (action_sp)
8189   {
8190     case actActive:             return ACTION_ACTIVE;
8191     case actImpact:             return ACTION_IMPACT;
8192     case actExploding:          return ACTION_EXPLODING;
8193     case actDigging:            return ACTION_DIGGING;
8194     case actSnapping:           return ACTION_SNAPPING;
8195     case actCollecting:         return ACTION_COLLECTING;
8196     case actPassing:            return ACTION_PASSING;
8197     case actPushing:            return ACTION_PUSHING;
8198     case actDropping:           return ACTION_DROPPING;
8199
8200     default:                    return ACTION_DEFAULT;
8201   }
8202 }
8203
8204 int map_element_RND_to_MM(int element_rnd)
8205 {
8206   return (element_rnd >= EL_MM_START_1 &&
8207           element_rnd <= EL_MM_END_1 ?
8208           EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
8209
8210           element_rnd >= EL_MM_START_2 &&
8211           element_rnd <= EL_MM_END_2 ?
8212           EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
8213
8214           element_rnd >= EL_MM_START_3 &&
8215           element_rnd <= EL_MM_END_3 ?
8216           EL_MM_START_3_NATIVE + element_rnd - EL_MM_START_3 :
8217
8218           element_rnd >= EL_CHAR_START &&
8219           element_rnd <= EL_CHAR_END ?
8220           EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
8221
8222           element_rnd >= EL_MM_RUNTIME_START &&
8223           element_rnd <= EL_MM_RUNTIME_END ?
8224           EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
8225
8226           EL_MM_EMPTY_NATIVE);
8227 }
8228
8229 int map_element_MM_to_RND(int element_mm)
8230 {
8231   return (element_mm == EL_MM_EMPTY_NATIVE ||
8232           element_mm == EL_DF_EMPTY_NATIVE ?
8233           EL_EMPTY :
8234
8235           element_mm >= EL_MM_START_1_NATIVE &&
8236           element_mm <= EL_MM_END_1_NATIVE ?
8237           EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
8238
8239           element_mm >= EL_MM_START_2_NATIVE &&
8240           element_mm <= EL_MM_END_2_NATIVE ?
8241           EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
8242
8243           element_mm >= EL_MM_START_3_NATIVE &&
8244           element_mm <= EL_MM_END_3_NATIVE ?
8245           EL_MM_START_3 + element_mm - EL_MM_START_3_NATIVE :
8246
8247           element_mm >= EL_MM_CHAR_START_NATIVE &&
8248           element_mm <= EL_MM_CHAR_END_NATIVE ?
8249           EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
8250
8251           element_mm >= EL_MM_RUNTIME_START_NATIVE &&
8252           element_mm <= EL_MM_RUNTIME_END_NATIVE ?
8253           EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
8254
8255           EL_EMPTY);
8256 }
8257
8258 int map_action_MM_to_RND(int action_mm)
8259 {
8260   // all MM actions are defined to exactly match their RND counterparts
8261   return action_mm;
8262 }
8263
8264 int map_sound_MM_to_RND(int sound_mm)
8265 {
8266   switch (sound_mm)
8267   {
8268     case SND_MM_GAME_LEVELTIME_CHARGING:
8269       return SND_GAME_LEVELTIME_CHARGING;
8270
8271     case SND_MM_GAME_HEALTH_CHARGING:
8272       return SND_GAME_HEALTH_CHARGING;
8273
8274     default:
8275       return SND_UNDEFINED;
8276   }
8277 }
8278
8279 int map_mm_wall_element(int element)
8280 {
8281   return (element >= EL_MM_STEEL_WALL_START &&
8282           element <= EL_MM_STEEL_WALL_END ?
8283           EL_MM_STEEL_WALL :
8284
8285           element >= EL_MM_WOODEN_WALL_START &&
8286           element <= EL_MM_WOODEN_WALL_END ?
8287           EL_MM_WOODEN_WALL :
8288
8289           element >= EL_MM_ICE_WALL_START &&
8290           element <= EL_MM_ICE_WALL_END ?
8291           EL_MM_ICE_WALL :
8292
8293           element >= EL_MM_AMOEBA_WALL_START &&
8294           element <= EL_MM_AMOEBA_WALL_END ?
8295           EL_MM_AMOEBA_WALL :
8296
8297           element >= EL_DF_STEEL_WALL_START &&
8298           element <= EL_DF_STEEL_WALL_END ?
8299           EL_DF_STEEL_WALL :
8300
8301           element >= EL_DF_WOODEN_WALL_START &&
8302           element <= EL_DF_WOODEN_WALL_END ?
8303           EL_DF_WOODEN_WALL :
8304
8305           element);
8306 }
8307
8308 int map_mm_wall_element_editor(int element)
8309 {
8310   switch (element)
8311   {
8312     case EL_MM_STEEL_WALL:      return EL_MM_STEEL_WALL_START;
8313     case EL_MM_WOODEN_WALL:     return EL_MM_WOODEN_WALL_START;
8314     case EL_MM_ICE_WALL:        return EL_MM_ICE_WALL_START;
8315     case EL_MM_AMOEBA_WALL:     return EL_MM_AMOEBA_WALL_START;
8316     case EL_DF_STEEL_WALL:      return EL_DF_STEEL_WALL_START;
8317     case EL_DF_WOODEN_WALL:     return EL_DF_WOODEN_WALL_START;
8318
8319     default:                    return element;
8320   }
8321 }
8322
8323 int get_next_element(int element)
8324 {
8325   switch (element)
8326   {
8327     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
8328     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
8329     case EL_QUICKSAND_FAST_FILLING:     return EL_QUICKSAND_FAST_FULL;
8330     case EL_QUICKSAND_FAST_EMPTYING:    return EL_QUICKSAND_FAST_EMPTY;
8331     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
8332     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
8333     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
8334     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
8335     case EL_DC_MAGIC_WALL_FILLING:      return EL_DC_MAGIC_WALL_FULL;
8336     case EL_DC_MAGIC_WALL_EMPTYING:     return EL_DC_MAGIC_WALL_ACTIVE;
8337     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
8338
8339     default:                            return element;
8340   }
8341 }
8342
8343 int el2img_mm(int element_mm)
8344 {
8345   return el2img(map_element_MM_to_RND(element_mm));
8346 }
8347
8348 int el_act2img_mm(int element_mm, int action)
8349 {
8350   return el_act2img(map_element_MM_to_RND(element_mm), action);
8351 }
8352
8353 int el_act_dir2img(int element, int action, int direction)
8354 {
8355   element = GFX_ELEMENT(element);
8356   direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8357
8358   // direction_graphic[][] == graphic[] for undefined direction graphics
8359   return element_info[element].direction_graphic[action][direction];
8360 }
8361
8362 static int el_act_dir2crm(int element, int action, int direction)
8363 {
8364   element = GFX_ELEMENT(element);
8365   direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8366
8367   // direction_graphic[][] == graphic[] for undefined direction graphics
8368   return element_info[element].direction_crumbled[action][direction];
8369 }
8370
8371 int el_act2img(int element, int action)
8372 {
8373   element = GFX_ELEMENT(element);
8374
8375   return element_info[element].graphic[action];
8376 }
8377
8378 int el_act2crm(int element, int action)
8379 {
8380   element = GFX_ELEMENT(element);
8381
8382   return element_info[element].crumbled[action];
8383 }
8384
8385 int el_dir2img(int element, int direction)
8386 {
8387   element = GFX_ELEMENT(element);
8388
8389   return el_act_dir2img(element, ACTION_DEFAULT, direction);
8390 }
8391
8392 int el2baseimg(int element)
8393 {
8394   return element_info[element].graphic[ACTION_DEFAULT];
8395 }
8396
8397 int el2img(int element)
8398 {
8399   element = GFX_ELEMENT(element);
8400
8401   return element_info[element].graphic[ACTION_DEFAULT];
8402 }
8403
8404 int el2edimg(int element)
8405 {
8406   element = GFX_ELEMENT(element);
8407
8408   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8409 }
8410
8411 int el2preimg(int element)
8412 {
8413   element = GFX_ELEMENT(element);
8414
8415   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8416 }
8417
8418 int el2panelimg(int element)
8419 {
8420   element = GFX_ELEMENT(element);
8421
8422   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8423 }
8424
8425 int font2baseimg(int font_nr)
8426 {
8427   return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8428 }
8429
8430 int getBeltNrFromBeltElement(int element)
8431 {
8432   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8433           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8434           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8435 }
8436
8437 int getBeltNrFromBeltActiveElement(int element)
8438 {
8439   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8440           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8441           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8442 }
8443
8444 int getBeltNrFromBeltSwitchElement(int element)
8445 {
8446   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8447           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8448           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8449 }
8450
8451 int getBeltDirNrFromBeltElement(int element)
8452 {
8453   static int belt_base_element[4] =
8454   {
8455     EL_CONVEYOR_BELT_1_LEFT,
8456     EL_CONVEYOR_BELT_2_LEFT,
8457     EL_CONVEYOR_BELT_3_LEFT,
8458     EL_CONVEYOR_BELT_4_LEFT
8459   };
8460
8461   int belt_nr = getBeltNrFromBeltElement(element);
8462   int belt_dir_nr = element - belt_base_element[belt_nr];
8463
8464   return (belt_dir_nr % 3);
8465 }
8466
8467 int getBeltDirNrFromBeltSwitchElement(int element)
8468 {
8469   static int belt_base_element[4] =
8470   {
8471     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8472     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8473     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8474     EL_CONVEYOR_BELT_4_SWITCH_LEFT
8475   };
8476
8477   int belt_nr = getBeltNrFromBeltSwitchElement(element);
8478   int belt_dir_nr = element - belt_base_element[belt_nr];
8479
8480   return (belt_dir_nr % 3);
8481 }
8482
8483 int getBeltDirFromBeltElement(int element)
8484 {
8485   static int belt_move_dir[3] =
8486   {
8487     MV_LEFT,
8488     MV_NONE,
8489     MV_RIGHT
8490   };
8491
8492   int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8493
8494   return belt_move_dir[belt_dir_nr];
8495 }
8496
8497 int getBeltDirFromBeltSwitchElement(int element)
8498 {
8499   static int belt_move_dir[3] =
8500   {
8501     MV_LEFT,
8502     MV_NONE,
8503     MV_RIGHT
8504   };
8505
8506   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8507
8508   return belt_move_dir[belt_dir_nr];
8509 }
8510
8511 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8512 {
8513   static int belt_base_element[4] =
8514   {
8515     EL_CONVEYOR_BELT_1_LEFT,
8516     EL_CONVEYOR_BELT_2_LEFT,
8517     EL_CONVEYOR_BELT_3_LEFT,
8518     EL_CONVEYOR_BELT_4_LEFT
8519   };
8520
8521   return belt_base_element[belt_nr] + belt_dir_nr;
8522 }
8523
8524 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8525 {
8526   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8527
8528   return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8529 }
8530
8531 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8532 {
8533   static int belt_base_element[4] =
8534   {
8535     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8536     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8537     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8538     EL_CONVEYOR_BELT_4_SWITCH_LEFT
8539   };
8540
8541   return belt_base_element[belt_nr] + belt_dir_nr;
8542 }
8543
8544 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8545 {
8546   int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8547
8548   return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8549 }
8550
8551 boolean swapTiles_EM(boolean is_pre_emc_cave)
8552 {
8553   return is_pre_emc_cave && leveldir_current->use_emc_tiles;
8554 }
8555
8556 boolean getTeamMode_EM(void)
8557 {
8558   return game.team_mode || network_playing;
8559 }
8560
8561 boolean isActivePlayer_EM(int player_nr)
8562 {
8563   return stored_player[player_nr].active;
8564 }
8565
8566 unsigned int InitRND(int seed)
8567 {
8568   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8569     return InitEngineRandom_EM(seed);
8570   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8571     return InitEngineRandom_SP(seed);
8572   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8573     return InitEngineRandom_MM(seed);
8574   else
8575     return InitEngineRandom_RND(seed);
8576 }
8577
8578 static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
8579 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8580
8581 static int get_effective_element_EM(int tile, int frame_em)
8582 {
8583   int element             = object_mapping[tile].element_rnd;
8584   int action              = object_mapping[tile].action;
8585   boolean is_backside     = object_mapping[tile].is_backside;
8586   boolean action_removing = (action == ACTION_DIGGING ||
8587                              action == ACTION_SNAPPING ||
8588                              action == ACTION_COLLECTING);
8589
8590   if (frame_em < 7)
8591   {
8592     switch (tile)
8593     {
8594       case Xsplash_e:
8595       case Xsplash_w:
8596         return (frame_em > 5 ? EL_EMPTY : element);
8597
8598       default:
8599         return element;
8600     }
8601   }
8602   else  // frame_em == 7
8603   {
8604     switch (tile)
8605     {
8606       case Xsplash_e:
8607       case Xsplash_w:
8608         return EL_EMPTY;
8609
8610       case Ynut_stone:
8611         return EL_EMERALD;
8612
8613       case Ydiamond_stone:
8614         return EL_ROCK;
8615
8616       case Xdrip_stretch:
8617       case Xdrip_stretchB:
8618       case Ydrip_1_s:
8619       case Ydrip_1_sB:
8620       case Yball_1:
8621       case Xball_2:
8622       case Yball_2:
8623       case Yball_blank:
8624       case Ykey_1_blank:
8625       case Ykey_2_blank:
8626       case Ykey_3_blank:
8627       case Ykey_4_blank:
8628       case Ykey_5_blank:
8629       case Ykey_6_blank:
8630       case Ykey_7_blank:
8631       case Ykey_8_blank:
8632       case Ylenses_blank:
8633       case Ymagnify_blank:
8634       case Ygrass_blank:
8635       case Ydirt_blank:
8636       case Xsand_stonein_1:
8637       case Xsand_stonein_2:
8638       case Xsand_stonein_3:
8639       case Xsand_stonein_4:
8640         return element;
8641
8642       default:
8643         return (is_backside || action_removing ? EL_EMPTY : element);
8644     }
8645   }
8646 }
8647
8648 static boolean check_linear_animation_EM(int tile)
8649 {
8650   switch (tile)
8651   {
8652     case Xsand_stonesand_1:
8653     case Xsand_stonesand_quickout_1:
8654     case Xsand_sandstone_1:
8655     case Xsand_stonein_1:
8656     case Xsand_stoneout_1:
8657     case Xboom_1:
8658     case Xdynamite_1:
8659     case Ybug_w_n:
8660     case Ybug_n_e:
8661     case Ybug_e_s:
8662     case Ybug_s_w:
8663     case Ybug_e_n:
8664     case Ybug_s_e:
8665     case Ybug_w_s:
8666     case Ybug_n_w:
8667     case Ytank_w_n:
8668     case Ytank_n_e:
8669     case Ytank_e_s:
8670     case Ytank_s_w:
8671     case Ytank_e_n:
8672     case Ytank_s_e:
8673     case Ytank_w_s:
8674     case Ytank_n_w:
8675     case Xsplash_e:
8676     case Xsplash_w:
8677     case Ynut_stone:
8678       return TRUE;
8679   }
8680
8681   return FALSE;
8682 }
8683
8684 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8685                                      boolean has_crumbled_graphics,
8686                                      int crumbled, int sync_frame)
8687 {
8688   // if element can be crumbled, but certain action graphics are just empty
8689   // space (like instantly snapping sand to empty space in 1 frame), do not
8690   // treat these empty space graphics as crumbled graphics in EMC engine
8691   if (crumbled == IMG_EMPTY_SPACE)
8692     has_crumbled_graphics = FALSE;
8693
8694   if (has_crumbled_graphics)
8695   {
8696     struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8697     int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8698                                            g_crumbled->anim_delay,
8699                                            g_crumbled->anim_mode,
8700                                            g_crumbled->anim_start_frame,
8701                                            sync_frame);
8702
8703     getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8704                      &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8705
8706     g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8707     g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8708
8709     g_em->has_crumbled_graphics = TRUE;
8710   }
8711   else
8712   {
8713     g_em->crumbled_bitmap = NULL;
8714     g_em->crumbled_src_x = 0;
8715     g_em->crumbled_src_y = 0;
8716     g_em->crumbled_border_size = 0;
8717     g_em->crumbled_tile_size = 0;
8718
8719     g_em->has_crumbled_graphics = FALSE;
8720   }
8721 }
8722
8723 #if 0
8724 void ResetGfxAnimation_EM(int x, int y, int tile)
8725 {
8726   GfxFrame[x][y] = 0;
8727 }
8728 #endif
8729
8730 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8731                         int tile, int frame_em, int x, int y)
8732 {
8733   int action = object_mapping[tile].action;
8734   int direction = object_mapping[tile].direction;
8735   int effective_element = get_effective_element_EM(tile, frame_em);
8736   int graphic = (direction == MV_NONE ?
8737                  el_act2img(effective_element, action) :
8738                  el_act_dir2img(effective_element, action, direction));
8739   struct GraphicInfo *g = &graphic_info[graphic];
8740   int sync_frame;
8741   boolean action_removing = (action == ACTION_DIGGING ||
8742                              action == ACTION_SNAPPING ||
8743                              action == ACTION_COLLECTING);
8744   boolean action_moving   = (action == ACTION_FALLING ||
8745                              action == ACTION_MOVING ||
8746                              action == ACTION_PUSHING ||
8747                              action == ACTION_EATING ||
8748                              action == ACTION_FILLING ||
8749                              action == ACTION_EMPTYING);
8750   boolean action_falling  = (action == ACTION_FALLING ||
8751                              action == ACTION_FILLING ||
8752                              action == ACTION_EMPTYING);
8753
8754   // special case: graphic uses "2nd movement tile" and has defined
8755   // 7 frames for movement animation (or less) => use default graphic
8756   // for last (8th) frame which ends the movement animation
8757   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8758   {
8759     action = ACTION_DEFAULT;    // (keep action_* unchanged for now)
8760     graphic = (direction == MV_NONE ?
8761                el_act2img(effective_element, action) :
8762                el_act_dir2img(effective_element, action, direction));
8763
8764     g = &graphic_info[graphic];
8765   }
8766
8767   if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8768   {
8769     GfxFrame[x][y] = 0;
8770   }
8771   else if (action_moving)
8772   {
8773     boolean is_backside = object_mapping[tile].is_backside;
8774
8775     if (is_backside)
8776     {
8777       int direction = object_mapping[tile].direction;
8778       int move_dir = (action_falling ? MV_DOWN : direction);
8779
8780       GfxFrame[x][y]++;
8781
8782 #if 1
8783       // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8784       if (g->double_movement && frame_em == 0)
8785         GfxFrame[x][y] = 0;
8786 #endif
8787
8788       if (move_dir == MV_LEFT)
8789         GfxFrame[x - 1][y] = GfxFrame[x][y];
8790       else if (move_dir == MV_RIGHT)
8791         GfxFrame[x + 1][y] = GfxFrame[x][y];
8792       else if (move_dir == MV_UP)
8793         GfxFrame[x][y - 1] = GfxFrame[x][y];
8794       else if (move_dir == MV_DOWN)
8795         GfxFrame[x][y + 1] = GfxFrame[x][y];
8796     }
8797   }
8798   else
8799   {
8800     GfxFrame[x][y]++;
8801
8802     // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8803     if (tile == Xsand_stonesand_quickout_1 ||
8804         tile == Xsand_stonesand_quickout_2)
8805       GfxFrame[x][y]++;
8806   }
8807
8808   if (graphic_info[graphic].anim_global_sync)
8809     sync_frame = FrameCounter;
8810   else if (graphic_info[graphic].anim_global_anim_sync)
8811     sync_frame = getGlobalAnimSyncFrame();
8812   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8813     sync_frame = GfxFrame[x][y];
8814   else
8815     sync_frame = 0;     // playfield border (pseudo steel)
8816
8817   SetRandomAnimationValue(x, y);
8818
8819   int frame = getAnimationFrame(g->anim_frames,
8820                                 g->anim_delay,
8821                                 g->anim_mode,
8822                                 g->anim_start_frame,
8823                                 sync_frame);
8824
8825   g_em->unique_identifier =
8826     (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8827 }
8828
8829 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8830                                   int tile, int frame_em, int x, int y)
8831 {
8832   int action = object_mapping[tile].action;
8833   int direction = object_mapping[tile].direction;
8834   boolean is_backside = object_mapping[tile].is_backside;
8835   int effective_element = get_effective_element_EM(tile, frame_em);
8836   int effective_action = action;
8837   int graphic = (direction == MV_NONE ?
8838                  el_act2img(effective_element, effective_action) :
8839                  el_act_dir2img(effective_element, effective_action,
8840                                 direction));
8841   int crumbled = (direction == MV_NONE ?
8842                   el_act2crm(effective_element, effective_action) :
8843                   el_act_dir2crm(effective_element, effective_action,
8844                                  direction));
8845   int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8846   int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8847   boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8848   struct GraphicInfo *g = &graphic_info[graphic];
8849   int sync_frame;
8850
8851   // special case: graphic uses "2nd movement tile" and has defined
8852   // 7 frames for movement animation (or less) => use default graphic
8853   // for last (8th) frame which ends the movement animation
8854   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8855   {
8856     effective_action = ACTION_DEFAULT;
8857     graphic = (direction == MV_NONE ?
8858                el_act2img(effective_element, effective_action) :
8859                el_act_dir2img(effective_element, effective_action,
8860                               direction));
8861     crumbled = (direction == MV_NONE ?
8862                 el_act2crm(effective_element, effective_action) :
8863                 el_act_dir2crm(effective_element, effective_action,
8864                                direction));
8865
8866     g = &graphic_info[graphic];
8867   }
8868
8869   if (graphic_info[graphic].anim_global_sync)
8870     sync_frame = FrameCounter;
8871   else if (graphic_info[graphic].anim_global_anim_sync)
8872     sync_frame = getGlobalAnimSyncFrame();
8873   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8874     sync_frame = GfxFrame[x][y];
8875   else
8876     sync_frame = 0;     // playfield border (pseudo steel)
8877
8878   SetRandomAnimationValue(x, y);
8879
8880   int frame = getAnimationFrame(g->anim_frames,
8881                                 g->anim_delay,
8882                                 g->anim_mode,
8883                                 g->anim_start_frame,
8884                                 sync_frame);
8885
8886   getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8887                       g->double_movement && is_backside);
8888
8889   // (updating the "crumbled" graphic definitions is probably not really needed,
8890   // as animations for crumbled graphics can't be longer than one EMC cycle)
8891   set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8892                            sync_frame);
8893 }
8894
8895 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8896                                   int player_nr, int anim, int frame_em)
8897 {
8898   int element   = player_mapping[player_nr][anim].element_rnd;
8899   int action    = player_mapping[player_nr][anim].action;
8900   int direction = player_mapping[player_nr][anim].direction;
8901   int graphic = (direction == MV_NONE ?
8902                  el_act2img(element, action) :
8903                  el_act_dir2img(element, action, direction));
8904   struct GraphicInfo *g = &graphic_info[graphic];
8905   int sync_frame;
8906
8907   InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8908
8909   stored_player[player_nr].StepFrame = frame_em;
8910
8911   sync_frame = stored_player[player_nr].Frame;
8912
8913   int frame = getAnimationFrame(g->anim_frames,
8914                                 g->anim_delay,
8915                                 g->anim_mode,
8916                                 g->anim_start_frame,
8917                                 sync_frame);
8918
8919   getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8920                       &g_em->src_x, &g_em->src_y, FALSE);
8921 }
8922
8923 void InitGraphicInfo_EM(void)
8924 {
8925   int i, j, p;
8926
8927   // always start with reliable default values
8928   for (i = 0; i < GAME_TILE_MAX; i++)
8929   {
8930     object_mapping[i].element_rnd = EL_UNKNOWN;
8931     object_mapping[i].is_backside = FALSE;
8932     object_mapping[i].action = ACTION_DEFAULT;
8933     object_mapping[i].direction = MV_NONE;
8934   }
8935
8936   // always start with reliable default values
8937   for (p = 0; p < MAX_PLAYERS; p++)
8938   {
8939     for (i = 0; i < PLY_MAX; i++)
8940     {
8941       player_mapping[p][i].element_rnd = EL_UNKNOWN;
8942       player_mapping[p][i].action = ACTION_DEFAULT;
8943       player_mapping[p][i].direction = MV_NONE;
8944     }
8945   }
8946
8947   for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8948   {
8949     int e = em_object_mapping_list[i].element_em;
8950
8951     object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8952     object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8953
8954     if (em_object_mapping_list[i].action != -1)
8955       object_mapping[e].action = em_object_mapping_list[i].action;
8956
8957     if (em_object_mapping_list[i].direction != -1)
8958       object_mapping[e].direction =
8959         MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8960   }
8961
8962   for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8963   {
8964     int a = em_player_mapping_list[i].action_em;
8965     int p = em_player_mapping_list[i].player_nr;
8966
8967     player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8968
8969     if (em_player_mapping_list[i].action != -1)
8970       player_mapping[p][a].action = em_player_mapping_list[i].action;
8971
8972     if (em_player_mapping_list[i].direction != -1)
8973       player_mapping[p][a].direction =
8974         MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8975   }
8976
8977   for (i = 0; i < GAME_TILE_MAX; i++)
8978   {
8979     int element = object_mapping[i].element_rnd;
8980     int action = object_mapping[i].action;
8981     int direction = object_mapping[i].direction;
8982     boolean is_backside = object_mapping[i].is_backside;
8983     boolean action_exploding = ((action == ACTION_EXPLODING ||
8984                                  action == ACTION_SMASHED_BY_ROCK ||
8985                                  action == ACTION_SMASHED_BY_SPRING) &&
8986                                 element != EL_DIAMOND);
8987     boolean action_active = (action == ACTION_ACTIVE);
8988     boolean action_other = (action == ACTION_OTHER);
8989
8990     for (j = 0; j < 8; j++)
8991     {
8992       int effective_element = get_effective_element_EM(i, j);
8993       int effective_action = (j < 7 ? action :
8994                               i == Xdrip_stretch ? action :
8995                               i == Xdrip_stretchB ? action :
8996                               i == Ydrip_1_s ? action :
8997                               i == Ydrip_1_sB ? action :
8998                               i == Yball_1 ? action :
8999                               i == Xball_2 ? action :
9000                               i == Yball_2 ? action :
9001                               i == Yball_blank ? action :
9002                               i == Ykey_1_blank ? action :
9003                               i == Ykey_2_blank ? action :
9004                               i == Ykey_3_blank ? action :
9005                               i == Ykey_4_blank ? action :
9006                               i == Ykey_5_blank ? action :
9007                               i == Ykey_6_blank ? action :
9008                               i == Ykey_7_blank ? action :
9009                               i == Ykey_8_blank ? action :
9010                               i == Ylenses_blank ? action :
9011                               i == Ymagnify_blank ? action :
9012                               i == Ygrass_blank ? action :
9013                               i == Ydirt_blank ? action :
9014                               i == Xsand_stonein_1 ? action :
9015                               i == Xsand_stonein_2 ? action :
9016                               i == Xsand_stonein_3 ? action :
9017                               i == Xsand_stonein_4 ? action :
9018                               i == Xsand_stoneout_1 ? action :
9019                               i == Xsand_stoneout_2 ? action :
9020                               i == Xboom_android ? ACTION_EXPLODING :
9021                               action_exploding ? ACTION_EXPLODING :
9022                               action_active ? action :
9023                               action_other ? action :
9024                               ACTION_DEFAULT);
9025       int graphic = (el_act_dir2img(effective_element, effective_action,
9026                                     direction));
9027       int crumbled = (el_act_dir2crm(effective_element, effective_action,
9028                                      direction));
9029       int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
9030       int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
9031       boolean has_action_graphics = (graphic != base_graphic);
9032       boolean has_crumbled_graphics = (base_crumbled != base_graphic);
9033       struct GraphicInfo *g = &graphic_info[graphic];
9034       struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9035       Bitmap *src_bitmap;
9036       int src_x, src_y;
9037       // ensure to get symmetric 3-frame, 2-delay animations as used in EM
9038       boolean special_animation = (action != ACTION_DEFAULT &&
9039                                    g->anim_frames == 3 &&
9040                                    g->anim_delay == 2 &&
9041                                    g->anim_mode & ANIM_LINEAR);
9042       int sync_frame = (i == Xdrip_stretch ? 7 :
9043                         i == Xdrip_stretchB ? 7 :
9044                         i == Ydrip_2_s ? j + 8 :
9045                         i == Ydrip_2_sB ? j + 8 :
9046                         i == Xacid_1 ? 0 :
9047                         i == Xacid_2 ? 10 :
9048                         i == Xacid_3 ? 20 :
9049                         i == Xacid_4 ? 30 :
9050                         i == Xacid_5 ? 40 :
9051                         i == Xacid_6 ? 50 :
9052                         i == Xacid_7 ? 60 :
9053                         i == Xacid_8 ? 70 :
9054                         i == Xfake_acid_1 ? 0 :
9055                         i == Xfake_acid_2 ? 10 :
9056                         i == Xfake_acid_3 ? 20 :
9057                         i == Xfake_acid_4 ? 30 :
9058                         i == Xfake_acid_5 ? 40 :
9059                         i == Xfake_acid_6 ? 50 :
9060                         i == Xfake_acid_7 ? 60 :
9061                         i == Xfake_acid_8 ? 70 :
9062                         i == Xfake_acid_1_player ? 0 :
9063                         i == Xfake_acid_2_player ? 10 :
9064                         i == Xfake_acid_3_player ? 20 :
9065                         i == Xfake_acid_4_player ? 30 :
9066                         i == Xfake_acid_5_player ? 40 :
9067                         i == Xfake_acid_6_player ? 50 :
9068                         i == Xfake_acid_7_player ? 60 :
9069                         i == Xfake_acid_8_player ? 70 :
9070                         i == Xball_2 ? 7 :
9071                         i == Yball_2 ? j + 8 :
9072                         i == Yball_blank ? j + 1 :
9073                         i == Ykey_1_blank ? j + 1 :
9074                         i == Ykey_2_blank ? j + 1 :
9075                         i == Ykey_3_blank ? j + 1 :
9076                         i == Ykey_4_blank ? j + 1 :
9077                         i == Ykey_5_blank ? j + 1 :
9078                         i == Ykey_6_blank ? j + 1 :
9079                         i == Ykey_7_blank ? j + 1 :
9080                         i == Ykey_8_blank ? j + 1 :
9081                         i == Ylenses_blank ? j + 1 :
9082                         i == Ymagnify_blank ? j + 1 :
9083                         i == Ygrass_blank ? j + 1 :
9084                         i == Ydirt_blank ? j + 1 :
9085                         i == Xamoeba_1 ? 0 :
9086                         i == Xamoeba_2 ? 1 :
9087                         i == Xamoeba_3 ? 2 :
9088                         i == Xamoeba_4 ? 3 :
9089                         i == Xamoeba_5 ? 0 :
9090                         i == Xamoeba_6 ? 1 :
9091                         i == Xamoeba_7 ? 2 :
9092                         i == Xamoeba_8 ? 3 :
9093                         i == Xexit_2 ? j + 8 :
9094                         i == Xexit_3 ? j + 16 :
9095                         i == Xdynamite_1 ? 0 :
9096                         i == Xdynamite_2 ? 8 :
9097                         i == Xdynamite_3 ? 16 :
9098                         i == Xdynamite_4 ? 24 :
9099                         i == Xsand_stonein_1 ? j + 1 :
9100                         i == Xsand_stonein_2 ? j + 9 :
9101                         i == Xsand_stonein_3 ? j + 17 :
9102                         i == Xsand_stonein_4 ? j + 25 :
9103                         i == Xsand_stoneout_1 && j == 0 ? 0 :
9104                         i == Xsand_stoneout_1 && j == 1 ? 0 :
9105                         i == Xsand_stoneout_1 && j == 2 ? 1 :
9106                         i == Xsand_stoneout_1 && j == 3 ? 2 :
9107                         i == Xsand_stoneout_1 && j == 4 ? 2 :
9108                         i == Xsand_stoneout_1 && j == 5 ? 3 :
9109                         i == Xsand_stoneout_1 && j == 6 ? 4 :
9110                         i == Xsand_stoneout_1 && j == 7 ? 4 :
9111                         i == Xsand_stoneout_2 && j == 0 ? 5 :
9112                         i == Xsand_stoneout_2 && j == 1 ? 6 :
9113                         i == Xsand_stoneout_2 && j == 2 ? 7 :
9114                         i == Xsand_stoneout_2 && j == 3 ? 8 :
9115                         i == Xsand_stoneout_2 && j == 4 ? 9 :
9116                         i == Xsand_stoneout_2 && j == 5 ? 11 :
9117                         i == Xsand_stoneout_2 && j == 6 ? 13 :
9118                         i == Xsand_stoneout_2 && j == 7 ? 15 :
9119                         i == Xboom_bug && j == 1 ? 2 :
9120                         i == Xboom_bug && j == 2 ? 2 :
9121                         i == Xboom_bug && j == 3 ? 4 :
9122                         i == Xboom_bug && j == 4 ? 4 :
9123                         i == Xboom_bug && j == 5 ? 2 :
9124                         i == Xboom_bug && j == 6 ? 2 :
9125                         i == Xboom_bug && j == 7 ? 0 :
9126                         i == Xboom_tank && j == 1 ? 2 :
9127                         i == Xboom_tank && j == 2 ? 2 :
9128                         i == Xboom_tank && j == 3 ? 4 :
9129                         i == Xboom_tank && j == 4 ? 4 :
9130                         i == Xboom_tank && j == 5 ? 2 :
9131                         i == Xboom_tank && j == 6 ? 2 :
9132                         i == Xboom_tank && j == 7 ? 0 :
9133                         i == Xboom_android && j == 7 ? 6 :
9134                         i == Xboom_1 && j == 1 ? 2 :
9135                         i == Xboom_1 && j == 2 ? 2 :
9136                         i == Xboom_1 && j == 3 ? 4 :
9137                         i == Xboom_1 && j == 4 ? 4 :
9138                         i == Xboom_1 && j == 5 ? 6 :
9139                         i == Xboom_1 && j == 6 ? 6 :
9140                         i == Xboom_1 && j == 7 ? 8 :
9141                         i == Xboom_2 && j == 0 ? 8 :
9142                         i == Xboom_2 && j == 1 ? 8 :
9143                         i == Xboom_2 && j == 2 ? 10 :
9144                         i == Xboom_2 && j == 3 ? 10 :
9145                         i == Xboom_2 && j == 4 ? 10 :
9146                         i == Xboom_2 && j == 5 ? 12 :
9147                         i == Xboom_2 && j == 6 ? 12 :
9148                         i == Xboom_2 && j == 7 ? 12 :
9149                         special_animation && j == 4 ? 3 :
9150                         effective_action != action ? 0 :
9151                         j);
9152       int frame = getAnimationFrame(g->anim_frames,
9153                                     g->anim_delay,
9154                                     g->anim_mode,
9155                                     g->anim_start_frame,
9156                                     sync_frame);
9157
9158       getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
9159                           g->double_movement && is_backside);
9160
9161       g_em->bitmap = src_bitmap;
9162       g_em->src_x = src_x;
9163       g_em->src_y = src_y;
9164       g_em->src_offset_x = 0;
9165       g_em->src_offset_y = 0;
9166       g_em->dst_offset_x = 0;
9167       g_em->dst_offset_y = 0;
9168       g_em->width  = TILEX;
9169       g_em->height = TILEY;
9170
9171       g_em->preserve_background = FALSE;
9172
9173       set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
9174                                sync_frame);
9175
9176       if ((!g->double_movement && (effective_action == ACTION_FALLING ||
9177                                    effective_action == ACTION_MOVING  ||
9178                                    effective_action == ACTION_PUSHING ||
9179                                    effective_action == ACTION_EATING)) ||
9180           (!has_action_graphics && (effective_action == ACTION_FILLING ||
9181                                     effective_action == ACTION_EMPTYING)))
9182       {
9183         int move_dir =
9184           (effective_action == ACTION_FALLING ||
9185            effective_action == ACTION_FILLING ||
9186            effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
9187         int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
9188         int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? 1 : 0);
9189         int num_steps = (i == Ydrip_1_s  ? 16 :
9190                          i == Ydrip_1_sB ? 16 :
9191                          i == Ydrip_2_s  ? 16 :
9192                          i == Ydrip_2_sB ? 16 :
9193                          i == Xsand_stonein_1 ? 32 :
9194                          i == Xsand_stonein_2 ? 32 :
9195                          i == Xsand_stonein_3 ? 32 :
9196                          i == Xsand_stonein_4 ? 32 :
9197                          i == Xsand_stoneout_1 ? 16 :
9198                          i == Xsand_stoneout_2 ? 16 : 8);
9199         int cx = ABS(dx) * (TILEX / num_steps);
9200         int cy = ABS(dy) * (TILEY / num_steps);
9201         int step_frame = (i == Ydrip_2_s        ? j + 8 :
9202                           i == Ydrip_2_sB       ? j + 8 :
9203                           i == Xsand_stonein_2  ? j + 8 :
9204                           i == Xsand_stonein_3  ? j + 16 :
9205                           i == Xsand_stonein_4  ? j + 24 :
9206                           i == Xsand_stoneout_2 ? j + 8 : j) + 1;
9207         int step = (is_backside ? step_frame : num_steps - step_frame);
9208
9209         if (is_backside)        // tile where movement starts
9210         {
9211           if (dx < 0 || dy < 0)
9212           {
9213             g_em->src_offset_x = cx * step;
9214             g_em->src_offset_y = cy * step;
9215           }
9216           else
9217           {
9218             g_em->dst_offset_x = cx * step;
9219             g_em->dst_offset_y = cy * step;
9220           }
9221         }
9222         else                    // tile where movement ends
9223         {
9224           if (dx < 0 || dy < 0)
9225           {
9226             g_em->dst_offset_x = cx * step;
9227             g_em->dst_offset_y = cy * step;
9228           }
9229           else
9230           {
9231             g_em->src_offset_x = cx * step;
9232             g_em->src_offset_y = cy * step;
9233           }
9234         }
9235
9236         g_em->width  = TILEX - cx * step;
9237         g_em->height = TILEY - cy * step;
9238       }
9239
9240       // create unique graphic identifier to decide if tile must be redrawn
9241       /* bit 31 - 16 (16 bit): EM style graphic
9242          bit 15 - 12 ( 4 bit): EM style frame
9243          bit 11 -  6 ( 6 bit): graphic width
9244          bit  5 -  0 ( 6 bit): graphic height */
9245       g_em->unique_identifier =
9246         (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
9247     }
9248   }
9249
9250   for (i = 0; i < GAME_TILE_MAX; i++)
9251   {
9252     for (j = 0; j < 8; j++)
9253     {
9254       int element = object_mapping[i].element_rnd;
9255       int action = object_mapping[i].action;
9256       int direction = object_mapping[i].direction;
9257       boolean is_backside = object_mapping[i].is_backside;
9258       int graphic_action  = el_act_dir2img(element, action, direction);
9259       int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9260
9261       if ((action == ACTION_SMASHED_BY_ROCK ||
9262            action == ACTION_SMASHED_BY_SPRING ||
9263            action == ACTION_EATING) &&
9264           graphic_action == graphic_default)
9265       {
9266         int e = (action == ACTION_SMASHED_BY_ROCK   ? Ystone_s  :
9267                  action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9268                  direction == MV_LEFT  ? (is_backside? Yspring_wB: Yspring_w) :
9269                  direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9270                  Xspring);
9271
9272         // no separate animation for "smashed by rock" -- use rock instead
9273         struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][j];
9274         struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][j];
9275
9276         g_em->bitmap            = g_xx->bitmap;
9277         g_em->src_x             = g_xx->src_x;
9278         g_em->src_y             = g_xx->src_y;
9279         g_em->src_offset_x      = g_xx->src_offset_x;
9280         g_em->src_offset_y      = g_xx->src_offset_y;
9281         g_em->dst_offset_x      = g_xx->dst_offset_x;
9282         g_em->dst_offset_y      = g_xx->dst_offset_y;
9283         g_em->width             = g_xx->width;
9284         g_em->height            = g_xx->height;
9285         g_em->unique_identifier = g_xx->unique_identifier;
9286
9287         if (!is_backside)
9288           g_em->preserve_background = TRUE;
9289       }
9290     }
9291   }
9292
9293   for (p = 0; p < MAX_PLAYERS; p++)
9294   {
9295     for (i = 0; i < PLY_MAX; i++)
9296     {
9297       int element = player_mapping[p][i].element_rnd;
9298       int action = player_mapping[p][i].action;
9299       int direction = player_mapping[p][i].direction;
9300
9301       for (j = 0; j < 8; j++)
9302       {
9303         int effective_element = element;
9304         int effective_action = action;
9305         int graphic = (direction == MV_NONE ?
9306                        el_act2img(effective_element, effective_action) :
9307                        el_act_dir2img(effective_element, effective_action,
9308                                       direction));
9309         struct GraphicInfo *g = &graphic_info[graphic];
9310         struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][j];
9311         Bitmap *src_bitmap;
9312         int src_x, src_y;
9313         int sync_frame = j;
9314         int frame = getAnimationFrame(g->anim_frames,
9315                                       g->anim_delay,
9316                                       g->anim_mode,
9317                                       g->anim_start_frame,
9318                                       sync_frame);
9319
9320         getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9321
9322         g_em->bitmap = src_bitmap;
9323         g_em->src_x = src_x;
9324         g_em->src_y = src_y;
9325         g_em->src_offset_x = 0;
9326         g_em->src_offset_y = 0;
9327         g_em->dst_offset_x = 0;
9328         g_em->dst_offset_y = 0;
9329         g_em->width  = TILEX;
9330         g_em->height = TILEY;
9331       }
9332     }
9333   }
9334 }
9335
9336 static void CheckSaveEngineSnapshot_EM(int frame,
9337                                        boolean any_player_moving,
9338                                        boolean any_player_snapping,
9339                                        boolean any_player_dropping)
9340 {
9341   if (frame == 7 && !any_player_dropping)
9342   {
9343     if (!local_player->was_waiting)
9344     {
9345       if (!CheckSaveEngineSnapshotToList())
9346         return;
9347
9348       local_player->was_waiting = TRUE;
9349     }
9350   }
9351   else if (any_player_moving || any_player_snapping || any_player_dropping)
9352   {
9353     local_player->was_waiting = FALSE;
9354   }
9355 }
9356
9357 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9358                                        boolean murphy_is_dropping)
9359 {
9360   if (murphy_is_waiting)
9361   {
9362     if (!local_player->was_waiting)
9363     {
9364       if (!CheckSaveEngineSnapshotToList())
9365         return;
9366
9367       local_player->was_waiting = TRUE;
9368     }
9369   }
9370   else
9371   {
9372     local_player->was_waiting = FALSE;
9373   }
9374 }
9375
9376 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9377                                        boolean button_released)
9378 {
9379   if (button_released)
9380   {
9381     if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9382       CheckSaveEngineSnapshotToList();
9383   }
9384   else if (element_clicked)
9385   {
9386     if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9387       CheckSaveEngineSnapshotToList();
9388
9389     game.snapshot.changed_action = TRUE;
9390   }
9391 }
9392
9393 boolean CheckSingleStepMode_EM(int frame,
9394                                boolean any_player_moving,
9395                                boolean any_player_snapping,
9396                                boolean any_player_dropping)
9397 {
9398   if (tape.single_step && tape.recording && !tape.pausing)
9399     if (frame == 7 && !any_player_dropping && FrameCounter > 6)
9400       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9401
9402   CheckSaveEngineSnapshot_EM(frame, any_player_moving,
9403                              any_player_snapping, any_player_dropping);
9404
9405   return tape.pausing;
9406 }
9407
9408 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9409                             boolean murphy_is_dropping)
9410 {
9411   boolean murphy_starts_dropping = FALSE;
9412   int i;
9413
9414   for (i = 0; i < MAX_PLAYERS; i++)
9415     if (stored_player[i].force_dropping)
9416       murphy_starts_dropping = TRUE;
9417
9418   if (tape.single_step && tape.recording && !tape.pausing)
9419     if (murphy_is_waiting && !murphy_starts_dropping)
9420       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9421
9422   CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9423 }
9424
9425 void CheckSingleStepMode_MM(boolean element_clicked,
9426                             boolean button_released)
9427 {
9428   if (tape.single_step && tape.recording && !tape.pausing)
9429     if (button_released)
9430       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9431
9432   CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9433 }
9434
9435 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9436                          int graphic, int sync_frame)
9437 {
9438   int frame = getGraphicAnimationFrame(graphic, sync_frame);
9439
9440   getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9441 }
9442
9443 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9444 {
9445   return (IS_NEXT_FRAME(sync_frame, graphic));
9446 }
9447
9448 int getGraphicInfo_Delay(int graphic)
9449 {
9450   return graphic_info[graphic].anim_delay;
9451 }
9452
9453 boolean getGraphicInfo_NewFrame(int x, int y, int graphic)
9454 {
9455   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
9456     return FALSE;
9457
9458   if (ANIM_MODE(graphic) & (ANIM_TILED | ANIM_RANDOM_STATIC))
9459     return FALSE;
9460
9461   return TRUE;
9462 }
9463
9464 void PlayMenuSoundExt(int sound)
9465 {
9466   if (sound == SND_UNDEFINED)
9467     return;
9468
9469   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9470       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9471     return;
9472
9473   if (IS_LOOP_SOUND(sound))
9474     PlaySoundLoop(sound);
9475   else
9476     PlaySound(sound);
9477 }
9478
9479 void PlayMenuSound(void)
9480 {
9481   PlayMenuSoundExt(menu.sound[game_status]);
9482 }
9483
9484 void PlayMenuSoundStereo(int sound, int stereo_position)
9485 {
9486   if (sound == SND_UNDEFINED)
9487     return;
9488
9489   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9490       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9491     return;
9492
9493   if (IS_LOOP_SOUND(sound))
9494     PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9495   else
9496     PlaySoundStereo(sound, stereo_position);
9497 }
9498
9499 void PlayMenuSoundIfLoopExt(int sound)
9500 {
9501   if (sound == SND_UNDEFINED)
9502     return;
9503
9504   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9505       (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9506     return;
9507
9508   if (IS_LOOP_SOUND(sound))
9509     PlaySoundLoop(sound);
9510 }
9511
9512 void PlayMenuSoundIfLoop(void)
9513 {
9514   PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9515 }
9516
9517 void PlayMenuMusicExt(int music)
9518 {
9519   if (music == MUS_UNDEFINED)
9520     return;
9521
9522   if (!setup.sound_music)
9523     return;
9524
9525   if (IS_LOOP_MUSIC(music))
9526     PlayMusicLoop(music);
9527   else
9528     PlayMusic(music);
9529 }
9530
9531 void PlayMenuMusic(void)
9532 {
9533   char *curr_music = getCurrentlyPlayingMusicFilename();
9534   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9535
9536   if (!strEqual(curr_music, next_music))
9537     PlayMenuMusicExt(menu.music[game_status]);
9538 }
9539
9540 void PlayMenuSoundsAndMusic(void)
9541 {
9542   PlayMenuSound();
9543   PlayMenuMusic();
9544 }
9545
9546 static void FadeMenuSounds(void)
9547 {
9548   FadeSounds();
9549 }
9550
9551 static void FadeMenuMusic(void)
9552 {
9553   char *curr_music = getCurrentlyPlayingMusicFilename();
9554   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9555
9556   if (!strEqual(curr_music, next_music))
9557     FadeMusic();
9558 }
9559
9560 void FadeMenuSoundsAndMusic(void)
9561 {
9562   FadeMenuSounds();
9563   FadeMenuMusic();
9564 }
9565
9566 void PlaySoundActivating(void)
9567 {
9568 #if 0
9569   PlaySound(SND_MENU_ITEM_ACTIVATING);
9570 #endif
9571 }
9572
9573 void PlaySoundSelecting(void)
9574 {
9575 #if 0
9576   PlaySound(SND_MENU_ITEM_SELECTING);
9577 #endif
9578 }
9579
9580 void ToggleFullscreenIfNeeded(void)
9581 {
9582   // if setup and video fullscreen state are already matching, nothing do do
9583   if (setup.fullscreen == video.fullscreen_enabled ||
9584       !video.fullscreen_available)
9585     return;
9586
9587   SDLSetWindowFullscreen(setup.fullscreen);
9588
9589   // set setup value according to successfully changed fullscreen mode
9590   setup.fullscreen = video.fullscreen_enabled;
9591 }
9592
9593 void ChangeWindowScalingIfNeeded(void)
9594 {
9595   // if setup and video window scaling are already matching, nothing do do
9596   if (setup.window_scaling_percent == video.window_scaling_percent ||
9597       video.fullscreen_enabled)
9598     return;
9599
9600   SDLSetWindowScaling(setup.window_scaling_percent);
9601
9602   // set setup value according to successfully changed window scaling
9603   setup.window_scaling_percent = video.window_scaling_percent;
9604 }
9605
9606 void ChangeVsyncModeIfNeeded(void)
9607 {
9608   int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
9609   int video_vsync_mode = video.vsync_mode;
9610
9611   // if setup and video vsync mode are already matching, nothing do do
9612   if (setup_vsync_mode == video_vsync_mode)
9613     return;
9614
9615   // if renderer is using OpenGL, vsync mode can directly be changed
9616   SDLSetScreenVsyncMode(setup.vsync_mode);
9617
9618   // if vsync mode unchanged, try re-creating renderer to set vsync mode
9619   if (video.vsync_mode == video_vsync_mode)
9620   {
9621     Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9622
9623     // save backbuffer content which gets lost when re-creating screen
9624     BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9625
9626     // force re-creating screen and renderer to set new vsync mode
9627     video.fullscreen_enabled = !setup.fullscreen;
9628
9629     // when creating new renderer, destroy textures linked to old renderer
9630     FreeAllImageTextures();     // needs old renderer to free the textures
9631
9632     // re-create screen and renderer (including change of vsync mode)
9633     ChangeVideoModeIfNeeded(setup.fullscreen);
9634
9635     // set setup value according to successfully changed fullscreen mode
9636     setup.fullscreen = video.fullscreen_enabled;
9637
9638     // restore backbuffer content from temporary backbuffer backup bitmap
9639     BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9640     FreeBitmap(tmp_backbuffer);
9641
9642     // update visible window/screen
9643     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9644
9645     // when changing vsync mode, re-create textures for new renderer
9646     InitImageTextures();
9647   }
9648
9649   // set setup value according to successfully changed vsync mode
9650   setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
9651 }
9652
9653 static void JoinRectangles(int *x, int *y, int *width, int *height,
9654                            int x2, int y2, int width2, int height2)
9655 {
9656   // do not join with "off-screen" rectangle
9657   if (x2 == -1 || y2 == -1)
9658     return;
9659
9660   *x = MIN(*x, x2);
9661   *y = MIN(*y, y2);
9662   *width = MAX(*width, width2);
9663   *height = MAX(*height, height2);
9664 }
9665
9666 void SetAnimStatus(int anim_status_new)
9667 {
9668   if (anim_status_new == GAME_MODE_MAIN)
9669     anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9670   else if (anim_status_new == GAME_MODE_NAMES)
9671     anim_status_new = GAME_MODE_PSEUDO_NAMESONLY;
9672   else if (anim_status_new == GAME_MODE_SCORES)
9673     anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9674
9675   global.anim_status_next = anim_status_new;
9676
9677   // directly set screen modes that are entered without fading
9678   if ((global.anim_status      == GAME_MODE_PSEUDO_MAINONLY &&
9679        global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9680       (global.anim_status      == GAME_MODE_PSEUDO_TYPENAME &&
9681        global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY) ||
9682       (global.anim_status      == GAME_MODE_PSEUDO_NAMESONLY &&
9683        global.anim_status_next == GAME_MODE_PSEUDO_TYPENAMES) ||
9684       (global.anim_status      == GAME_MODE_PSEUDO_TYPENAMES &&
9685        global.anim_status_next == GAME_MODE_PSEUDO_NAMESONLY))
9686     global.anim_status = global.anim_status_next;
9687 }
9688
9689 void SetGameStatus(int game_status_new)
9690 {
9691   if (game_status_new != game_status)
9692     game_status_last_screen = game_status;
9693
9694   game_status = game_status_new;
9695
9696   SetAnimStatus(game_status_new);
9697 }
9698
9699 void SetFontStatus(int game_status_new)
9700 {
9701   static int last_game_status = -1;
9702
9703   if (game_status_new != -1)
9704   {
9705     // set game status for font use after storing last game status
9706     last_game_status = game_status;
9707     game_status = game_status_new;
9708   }
9709   else
9710   {
9711     // reset game status after font use from last stored game status
9712     game_status = last_game_status;
9713   }
9714 }
9715
9716 void ResetFontStatus(void)
9717 {
9718   SetFontStatus(-1);
9719 }
9720
9721 void SetLevelSetInfo(char *identifier, int level_nr)
9722 {
9723   setString(&levelset.identifier, identifier);
9724
9725   levelset.level_nr = level_nr;
9726 }
9727
9728 boolean CheckIfAllViewportsHaveChanged(void)
9729 {
9730   // if game status has not changed, viewports have not changed either
9731   if (game_status == game_status_last)
9732     return FALSE;
9733
9734   // check if all viewports have changed with current game status
9735
9736   struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9737   struct RectWithBorder *vp_door_1    = &viewport.door_1[game_status];
9738   struct RectWithBorder *vp_door_2    = &viewport.door_2[game_status];
9739   int new_real_sx       = vp_playfield->x;
9740   int new_real_sy       = vp_playfield->y;
9741   int new_full_sxsize   = vp_playfield->width;
9742   int new_full_sysize   = vp_playfield->height;
9743   int new_dx            = vp_door_1->x;
9744   int new_dy            = vp_door_1->y;
9745   int new_dxsize        = vp_door_1->width;
9746   int new_dysize        = vp_door_1->height;
9747   int new_vx            = vp_door_2->x;
9748   int new_vy            = vp_door_2->y;
9749   int new_vxsize        = vp_door_2->width;
9750   int new_vysize        = vp_door_2->height;
9751
9752   boolean playfield_viewport_has_changed =
9753     (new_real_sx != REAL_SX ||
9754      new_real_sy != REAL_SY ||
9755      new_full_sxsize != FULL_SXSIZE ||
9756      new_full_sysize != FULL_SYSIZE);
9757
9758   boolean door_1_viewport_has_changed =
9759     (new_dx != DX ||
9760      new_dy != DY ||
9761      new_dxsize != DXSIZE ||
9762      new_dysize != DYSIZE);
9763
9764   boolean door_2_viewport_has_changed =
9765     (new_vx != VX ||
9766      new_vy != VY ||
9767      new_vxsize != VXSIZE ||
9768      new_vysize != VYSIZE ||
9769      game_status_last == GAME_MODE_EDITOR);
9770
9771   return (playfield_viewport_has_changed &&
9772           door_1_viewport_has_changed &&
9773           door_2_viewport_has_changed);
9774 }
9775
9776 boolean CheckFadeAll(void)
9777 {
9778   return (CheckIfGlobalBorderHasChanged() ||
9779           CheckIfAllViewportsHaveChanged());
9780 }
9781
9782 void ChangeViewportPropertiesIfNeeded(void)
9783 {
9784   boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9785                                FALSE : setup.small_game_graphics);
9786   int gfx_game_mode = getGlobalGameStatus(game_status);
9787   int gfx_game_mode2 = (gfx_game_mode == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9788                         gfx_game_mode);
9789   struct RectWithBorder *vp_window    = &viewport.window[gfx_game_mode];
9790   struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9791   struct RectWithBorder *vp_door_1    = &viewport.door_1[gfx_game_mode];
9792   struct RectWithBorder *vp_door_2    = &viewport.door_2[gfx_game_mode2];
9793   struct RectWithBorder *vp_door_3    = &viewport.door_2[GAME_MODE_EDITOR];
9794   int new_win_xsize     = vp_window->width;
9795   int new_win_ysize     = vp_window->height;
9796   int border_left       = vp_playfield->border_left;
9797   int border_right      = vp_playfield->border_right;
9798   int border_top        = vp_playfield->border_top;
9799   int border_bottom     = vp_playfield->border_bottom;
9800   int new_sx            = vp_playfield->x      + border_left;
9801   int new_sy            = vp_playfield->y      + border_top;
9802   int new_sxsize        = vp_playfield->width  - border_left - border_right;
9803   int new_sysize        = vp_playfield->height - border_top  - border_bottom;
9804   int new_real_sx       = vp_playfield->x;
9805   int new_real_sy       = vp_playfield->y;
9806   int new_full_sxsize   = vp_playfield->width;
9807   int new_full_sysize   = vp_playfield->height;
9808   int new_dx            = vp_door_1->x;
9809   int new_dy            = vp_door_1->y;
9810   int new_dxsize        = vp_door_1->width;
9811   int new_dysize        = vp_door_1->height;
9812   int new_vx            = vp_door_2->x;
9813   int new_vy            = vp_door_2->y;
9814   int new_vxsize        = vp_door_2->width;
9815   int new_vysize        = vp_door_2->height;
9816   int new_ex            = vp_door_3->x;
9817   int new_ey            = vp_door_3->y;
9818   int new_exsize        = vp_door_3->width;
9819   int new_eysize        = vp_door_3->height;
9820   int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9821   int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9822                   gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9823   int new_scr_fieldx = new_sxsize / tilesize;
9824   int new_scr_fieldy = new_sysize / tilesize;
9825   int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9826   int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9827   boolean init_gfx_buffers = FALSE;
9828   boolean init_video_buffer = FALSE;
9829   boolean init_gadgets_and_anims = FALSE;
9830   boolean init_em_graphics = FALSE;
9831
9832   if (new_win_xsize != WIN_XSIZE ||
9833       new_win_ysize != WIN_YSIZE)
9834   {
9835     WIN_XSIZE = new_win_xsize;
9836     WIN_YSIZE = new_win_ysize;
9837
9838     init_video_buffer = TRUE;
9839     init_gfx_buffers = TRUE;
9840     init_gadgets_and_anims = TRUE;
9841
9842     // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
9843   }
9844
9845   if (new_scr_fieldx != SCR_FIELDX ||
9846       new_scr_fieldy != SCR_FIELDY)
9847   {
9848     // this always toggles between MAIN and GAME when using small tile size
9849
9850     SCR_FIELDX = new_scr_fieldx;
9851     SCR_FIELDY = new_scr_fieldy;
9852
9853     // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
9854   }
9855
9856   if (new_sx != SX ||
9857       new_sy != SY ||
9858       new_dx != DX ||
9859       new_dy != DY ||
9860       new_vx != VX ||
9861       new_vy != VY ||
9862       new_ex != EX ||
9863       new_ey != EY ||
9864       new_sxsize != SXSIZE ||
9865       new_sysize != SYSIZE ||
9866       new_dxsize != DXSIZE ||
9867       new_dysize != DYSIZE ||
9868       new_vxsize != VXSIZE ||
9869       new_vysize != VYSIZE ||
9870       new_exsize != EXSIZE ||
9871       new_eysize != EYSIZE ||
9872       new_real_sx != REAL_SX ||
9873       new_real_sy != REAL_SY ||
9874       new_full_sxsize != FULL_SXSIZE ||
9875       new_full_sysize != FULL_SYSIZE ||
9876       new_tilesize_var != TILESIZE_VAR
9877       )
9878   {
9879     // ------------------------------------------------------------------------
9880     // determine next fading area for changed viewport definitions
9881     // ------------------------------------------------------------------------
9882
9883     // start with current playfield area (default fading area)
9884     FADE_SX = REAL_SX;
9885     FADE_SY = REAL_SY;
9886     FADE_SXSIZE = FULL_SXSIZE;
9887     FADE_SYSIZE = FULL_SYSIZE;
9888
9889     // add new playfield area if position or size has changed
9890     if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9891         new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9892     {
9893       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9894                      new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9895     }
9896
9897     // add current and new door 1 area if position or size has changed
9898     if (new_dx != DX || new_dy != DY ||
9899         new_dxsize != DXSIZE || new_dysize != DYSIZE)
9900     {
9901       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9902                      DX, DY, DXSIZE, DYSIZE);
9903       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9904                      new_dx, new_dy, new_dxsize, new_dysize);
9905     }
9906
9907     // add current and new door 2 area if position or size has changed
9908     if (new_vx != VX || new_vy != VY ||
9909         new_vxsize != VXSIZE || new_vysize != VYSIZE)
9910     {
9911       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9912                      VX, VY, VXSIZE, VYSIZE);
9913       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9914                      new_vx, new_vy, new_vxsize, new_vysize);
9915     }
9916
9917     // ------------------------------------------------------------------------
9918     // handle changed tile size
9919     // ------------------------------------------------------------------------
9920
9921     if (new_tilesize_var != TILESIZE_VAR)
9922     {
9923       // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
9924
9925       // changing tile size invalidates scroll values of engine snapshots
9926       FreeEngineSnapshotSingle();
9927
9928       // changing tile size requires update of graphic mapping for EM engine
9929       init_em_graphics = TRUE;
9930     }
9931
9932     SX = new_sx;
9933     SY = new_sy;
9934     DX = new_dx;
9935     DY = new_dy;
9936     VX = new_vx;
9937     VY = new_vy;
9938     EX = new_ex;
9939     EY = new_ey;
9940     SXSIZE = new_sxsize;
9941     SYSIZE = new_sysize;
9942     DXSIZE = new_dxsize;
9943     DYSIZE = new_dysize;
9944     VXSIZE = new_vxsize;
9945     VYSIZE = new_vysize;
9946     EXSIZE = new_exsize;
9947     EYSIZE = new_eysize;
9948     REAL_SX = new_real_sx;
9949     REAL_SY = new_real_sy;
9950     FULL_SXSIZE = new_full_sxsize;
9951     FULL_SYSIZE = new_full_sysize;
9952     TILESIZE_VAR = new_tilesize_var;
9953
9954     init_gfx_buffers = TRUE;
9955     init_gadgets_and_anims = TRUE;
9956
9957     // Debug("tools:viewport", "viewports: init_gfx_buffers");
9958     // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
9959   }
9960
9961   if (init_gfx_buffers)
9962   {
9963     // Debug("tools:viewport", "init_gfx_buffers");
9964
9965     SCR_FIELDX = new_scr_fieldx_buffers;
9966     SCR_FIELDY = new_scr_fieldy_buffers;
9967
9968     InitGfxBuffers();
9969
9970     SCR_FIELDX = new_scr_fieldx;
9971     SCR_FIELDY = new_scr_fieldy;
9972
9973     SetDrawDeactivationMask(REDRAW_NONE);
9974     SetDrawBackgroundMask(REDRAW_FIELD);
9975   }
9976
9977   if (init_video_buffer)
9978   {
9979     // Debug("tools:viewport", "init_video_buffer");
9980
9981     FreeAllImageTextures();     // needs old renderer to free the textures
9982
9983     InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9984     InitImageTextures();
9985   }
9986
9987   if (init_gadgets_and_anims)
9988   {
9989     // Debug("tools:viewport", "init_gadgets_and_anims");
9990
9991     InitGadgets();
9992     InitGlobalAnimations();
9993   }
9994
9995   if (init_em_graphics)
9996   {
9997     InitGraphicInfo_EM();
9998   }
9999 }
10000
10001 void OpenURL(char *url)
10002 {
10003 #if SDL_VERSION_ATLEAST(2,0,14)
10004   SDL_OpenURL(url);
10005 #else
10006   Warn("SDL_OpenURL(\"%s\") not supported by SDL %d.%d.%d!",
10007        url, SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
10008   Warn("Please upgrade to at least SDL 2.0.14 for URL support!");
10009 #endif
10010 }
10011
10012 void OpenURLFromHash(SetupFileHash *hash, int hash_key)
10013 {
10014   OpenURL(getHashEntry(hash, int2str(hash_key, 0)));
10015 }
10016
10017
10018 // ============================================================================
10019 // tests
10020 // ============================================================================
10021
10022 #if defined(PLATFORM_WINDOWS)
10023 /* FILETIME of Jan 1 1970 00:00:00. */
10024 static const unsigned __int64 epoch = ((unsigned __int64) 116444736000000000ULL);
10025
10026 /*
10027  * timezone information is stored outside the kernel so tzp isn't used anymore.
10028  *
10029  * Note: this function is not for Win32 high precision timing purpose. See
10030  * elapsed_time().
10031  */
10032 static int gettimeofday_windows(struct timeval * tp, struct timezone * tzp)
10033 {
10034   FILETIME    file_time;
10035   SYSTEMTIME  system_time;
10036   ULARGE_INTEGER ularge;
10037
10038   GetSystemTime(&system_time);
10039   SystemTimeToFileTime(&system_time, &file_time);
10040   ularge.LowPart = file_time.dwLowDateTime;
10041   ularge.HighPart = file_time.dwHighDateTime;
10042
10043   tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
10044   tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
10045
10046   return 0;
10047 }
10048 #endif
10049
10050 static char *test_init_uuid_random_function_simple(void)
10051 {
10052   static char seed_text[100];
10053   unsigned int seed = InitSimpleRandom(NEW_RANDOMIZE);
10054
10055   sprintf(seed_text, "%d", seed);
10056
10057   return seed_text;
10058 }
10059
10060 static char *test_init_uuid_random_function_better(void)
10061 {
10062   static char seed_text[100];
10063   struct timeval current_time;
10064
10065   gettimeofday(&current_time, NULL);
10066
10067   prng_seed_bytes(&current_time, sizeof(current_time));
10068
10069   sprintf(seed_text, "%ld.%ld",
10070           (long)current_time.tv_sec,
10071           (long)current_time.tv_usec);
10072
10073   return seed_text;
10074 }
10075
10076 #if defined(PLATFORM_WINDOWS)
10077 static char *test_init_uuid_random_function_better_windows(void)
10078 {
10079   static char seed_text[100];
10080   struct timeval current_time;
10081
10082   gettimeofday_windows(&current_time, NULL);
10083
10084   prng_seed_bytes(&current_time, sizeof(current_time));
10085
10086   sprintf(seed_text, "%ld.%ld",
10087           (long)current_time.tv_sec,
10088           (long)current_time.tv_usec);
10089
10090   return seed_text;
10091 }
10092 #endif
10093
10094 static unsigned int test_uuid_random_function_simple(int max)
10095 {
10096   return GetSimpleRandom(max);
10097 }
10098
10099 static unsigned int test_uuid_random_function_better(int max)
10100 {
10101   return (max > 0 ? prng_get_uint() % max : 0);
10102 }
10103
10104 #if defined(PLATFORM_WINDOWS)
10105 #define NUM_UUID_TESTS                  3
10106 #else
10107 #define NUM_UUID_TESTS                  2
10108 #endif
10109
10110 static void TestGeneratingUUIDs_RunTest(int nr, int always_seed, int num_uuids)
10111 {
10112   struct hashtable *hash_seeds =
10113     create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10114   struct hashtable *hash_uuids =
10115     create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
10116   static char message[100];
10117   int i;
10118
10119   char *random_name = (nr == 0 ? "simple" : "better");
10120   char *random_type = (always_seed ? "always" : "only once");
10121   char *(*init_random_function)(void) =
10122     (nr == 0 ?
10123      test_init_uuid_random_function_simple :
10124      test_init_uuid_random_function_better);
10125   unsigned int (*random_function)(int) =
10126     (nr == 0 ?
10127      test_uuid_random_function_simple :
10128      test_uuid_random_function_better);
10129   int xpos = 40;
10130
10131 #if defined(PLATFORM_WINDOWS)
10132   if (nr == 2)
10133   {
10134     random_name = "windows";
10135     init_random_function = test_init_uuid_random_function_better_windows;
10136   }
10137 #endif
10138
10139   ClearField();
10140
10141   DrawTextF(xpos, 40, FC_GREEN, "Test: Generating UUIDs");
10142   DrawTextF(xpos, 80, FC_YELLOW, "Test %d.%d:", nr + 1, always_seed + 1);
10143
10144   DrawTextF(xpos, 100, FC_YELLOW, "Random Generator Name: %s", random_name);
10145   DrawTextF(xpos, 120, FC_YELLOW, "Seeding Random Generator: %s", random_type);
10146   DrawTextF(xpos, 140, FC_YELLOW, "Number of UUIDs generated: %d", num_uuids);
10147
10148   DrawTextF(xpos, 180, FC_GREEN, "Please wait ...");
10149
10150   BackToFront();
10151
10152   // always initialize random number generator at least once
10153   init_random_function();
10154
10155   unsigned int time_start = SDL_GetTicks();
10156
10157   for (i = 0; i < num_uuids; i++)
10158   {
10159     if (always_seed)
10160     {
10161       char *seed = getStringCopy(init_random_function());
10162
10163       hashtable_remove(hash_seeds, seed);
10164       hashtable_insert(hash_seeds, seed, "1");
10165     }
10166
10167     char *uuid = getStringCopy(getUUIDExt(random_function));
10168
10169     hashtable_remove(hash_uuids, uuid);
10170     hashtable_insert(hash_uuids, uuid, "1");
10171   }
10172
10173   int num_unique_seeds = hashtable_count(hash_seeds);
10174   int num_unique_uuids = hashtable_count(hash_uuids);
10175
10176   unsigned int time_needed = SDL_GetTicks() - time_start;
10177
10178   DrawTextF(xpos, 220, FC_YELLOW, "Time needed: %d ms", time_needed);
10179
10180   DrawTextF(xpos, 240, FC_YELLOW, "Number of unique UUIDs: %d", num_unique_uuids);
10181
10182   if (always_seed)
10183     DrawTextF(xpos, 260, FC_YELLOW, "Number of unique seeds: %d", num_unique_seeds);
10184
10185   if (nr == NUM_UUID_TESTS - 1 && always_seed)
10186     DrawTextF(xpos, 300, FC_GREEN, "All tests done!");
10187   else
10188     DrawTextF(xpos, 300, FC_GREEN, "Confirm dialog for next test ...");
10189
10190   sprintf(message, "Test %d.%d finished!", nr + 1, always_seed + 1);
10191
10192   Request(message, REQ_CONFIRM);
10193
10194   hashtable_destroy(hash_seeds, 0);
10195   hashtable_destroy(hash_uuids, 0);
10196 }
10197
10198 void TestGeneratingUUIDs(void)
10199 {
10200   int num_uuids = 1000000;
10201   int i, j;
10202
10203   for (i = 0; i < NUM_UUID_TESTS; i++)
10204     for (j = 0; j < 2; j++)
10205       TestGeneratingUUIDs_RunTest(i, j, num_uuids);
10206
10207   CloseAllAndExit(0);
10208 }