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