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