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