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