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