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