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