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