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