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