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