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