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