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