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