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