rnd-20040430-2-src
[rocksndiamonds.git] / src / tools.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * tools.c                                                  *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "tools.h"
17 #include "game.h"
18 #include "events.h"
19 #include "cartoons.h"
20 #include "network.h"
21 #include "tape.h"
22
23 /* tool button identifiers */
24 #define TOOL_CTRL_ID_YES        0
25 #define TOOL_CTRL_ID_NO         1
26 #define TOOL_CTRL_ID_CONFIRM    2
27 #define TOOL_CTRL_ID_PLAYER_1   3
28 #define TOOL_CTRL_ID_PLAYER_2   4
29 #define TOOL_CTRL_ID_PLAYER_3   5
30 #define TOOL_CTRL_ID_PLAYER_4   6
31
32 #define NUM_TOOL_BUTTONS        7
33
34 /* forward declaration for internal use */
35 static void UnmapToolButtons();
36 static void HandleToolButtons(struct GadgetInfo *);
37 static int el_act_dir2crm(int, int, int);
38 static int el_act2crm(int, int);
39
40 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
41 static int request_gadget_id = -1;
42
43 static char *print_if_not_empty(int element)
44 {
45   static char *s = NULL;
46   char *token_name = element_info[element].token_name;
47
48   if (s != NULL)
49     free(s);
50
51   s = checked_malloc(strlen(token_name) + 10 + 1);
52
53   if (element != EL_EMPTY)
54     sprintf(s, "%d\t['%s']", element, token_name);
55   else
56     sprintf(s, "%d", element);
57
58   return s;
59 }
60
61 void DumpTile(int x, int y)
62 {
63   int sx = SCREENX(x);
64   int sy = SCREENX(y);
65
66   printf_line("-", 79);
67   printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
68   printf_line("-", 79);
69
70   if (!IN_LEV_FIELD(x, y))
71   {
72     printf("(not in level field)\n");
73     printf("\n");
74
75     return;
76   }
77
78   printf("  Feld:        %d\t['%s']\n", Feld[x][y],
79          element_info[Feld[x][y]].token_name);
80   printf("  Back:        %s\n", print_if_not_empty(Back[x][y]));
81   printf("  Store:       %s\n", print_if_not_empty(Store[x][y]));
82   printf("  Store2:      %s\n", print_if_not_empty(Store2[x][y]));
83   printf("  StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
84   printf("  MovPos:      %d\n", MovPos[x][y]);
85   printf("  MovDir:      %d\n", MovDir[x][y]);
86   printf("  MovDelay:    %d\n", MovDelay[x][y]);
87   printf("  ChangeDelay: %d\n", ChangeDelay[x][y]);
88   printf("  GfxElement:  %d\n", GfxElement[x][y]);
89   printf("  GfxAction:   %d\n", GfxAction[x][y]);
90   printf("  GfxFrame:    %d\n", GfxFrame[x][y]);
91   printf("\n");
92 }
93
94 void SetDrawtoField(int mode)
95 {
96   if (mode == DRAW_BUFFERED && setup.soft_scrolling)
97   {
98     FX = TILEX;
99     FY = TILEY;
100     BX1 = -1;
101     BY1 = -1;
102     BX2 = SCR_FIELDX;
103     BY2 = SCR_FIELDY;
104     redraw_x1 = 1;
105     redraw_y1 = 1;
106
107     drawto_field = fieldbuffer;
108   }
109   else  /* DRAW_DIRECT, DRAW_BACKBUFFER */
110   {
111     FX = SX;
112     FY = SY;
113     BX1 = 0;
114     BY1 = 0;
115     BX2 = SCR_FIELDX - 1;
116     BY2 = SCR_FIELDY - 1;
117     redraw_x1 = 0;
118     redraw_y1 = 0;
119
120     drawto_field = (mode == DRAW_DIRECT ? window :  backbuffer);
121   }
122 }
123
124 void RedrawPlayfield(boolean force_redraw, int x, int y, int width, int height)
125 {
126   if (game_status == GAME_MODE_PLAYING && !game.envelope_active)
127   {
128     if (force_redraw)
129     {
130       x = gfx.sx - TILEX;
131       y = gfx.sy - TILEY;
132       width = gfx.sxsize + 2 * TILEX;
133       height = gfx.sysize + 2 * TILEY;
134     }
135
136     if (force_redraw || setup.direct_draw)
137     {
138       int xx, yy;
139       int x1 = (x - SX) / TILEX, y1 = (y - SY) / TILEY;
140       int x2 = (x - SX + width) / TILEX, y2 = (y - SY + height) / TILEY;
141
142       if (setup.direct_draw)
143         SetDrawtoField(DRAW_BACKBUFFER);
144
145       for (xx = BX1; xx <= BX2; xx++)
146         for (yy = BY1; yy <= BY2; yy++)
147           if (xx >= x1 && xx <= x2 && yy >= y1 && yy <= y2)
148             DrawScreenField(xx, yy);
149       DrawAllPlayers();
150
151       if (setup.direct_draw)
152         SetDrawtoField(DRAW_DIRECT);
153     }
154
155     if (setup.soft_scrolling)
156     {
157       int fx = FX, fy = FY;
158
159       fx += (ScreenMovDir & (MV_LEFT|MV_RIGHT) ? ScreenGfxPos : 0);
160       fy += (ScreenMovDir & (MV_UP|MV_DOWN)    ? ScreenGfxPos : 0);
161
162       BlitBitmap(fieldbuffer, backbuffer, fx,fy, SXSIZE,SYSIZE, SX,SY);
163     }
164   }
165
166   BlitBitmap(drawto, window, x, y, width, height, x, y);
167 }
168
169 void BackToFront()
170 {
171   int x,y;
172   DrawBuffer *buffer = (drawto_field == window ? backbuffer : drawto_field);
173
174   if (setup.direct_draw && game_status == GAME_MODE_PLAYING)
175     redraw_mask &= ~REDRAW_MAIN;
176
177   if (redraw_mask & REDRAW_TILES && redraw_tiles > REDRAWTILES_THRESHOLD)
178     redraw_mask |= REDRAW_FIELD;
179
180   if (redraw_mask & REDRAW_FIELD)
181     redraw_mask &= ~REDRAW_TILES;
182
183   if (redraw_mask == REDRAW_NONE)
184     return;
185
186   if (global.fps_slowdown && game_status == GAME_MODE_PLAYING)
187   {
188     static boolean last_frame_skipped = FALSE;
189     boolean skip_even_when_not_scrolling = TRUE;
190     boolean just_scrolling = (ScreenMovDir != 0);
191     boolean verbose = FALSE;
192
193     if (global.fps_slowdown_factor > 1 &&
194         (FrameCounter % global.fps_slowdown_factor) &&
195         (just_scrolling || skip_even_when_not_scrolling))
196     {
197       redraw_mask &= ~REDRAW_MAIN;
198
199       last_frame_skipped = TRUE;
200
201       if (verbose)
202         printf("FRAME SKIPPED\n");
203     }
204     else
205     {
206       if (last_frame_skipped)
207         redraw_mask |= REDRAW_FIELD;
208
209       last_frame_skipped = FALSE;
210
211       if (verbose)
212         printf("frame not skipped\n");
213     }
214   }
215
216   /* synchronize X11 graphics at this point; if we would synchronize the
217      display immediately after the buffer switching (after the XFlush),
218      this could mean that we have to wait for the graphics to complete,
219      although we could go on doing calculations for the next frame */
220
221   SyncDisplay();
222
223   if (redraw_mask & REDRAW_ALL)
224   {
225     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
226     redraw_mask = 0;
227   }
228
229   if (redraw_mask & REDRAW_FIELD)
230   {
231     if (game_status != GAME_MODE_PLAYING ||
232         redraw_mask & REDRAW_FROM_BACKBUFFER)
233     {
234       BlitBitmap(backbuffer, window,
235                  REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
236     }
237     else
238     {
239       int fx = FX, fy = FY;
240
241       if (setup.soft_scrolling)
242       {
243         fx += (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
244         fy += (ScreenMovDir & (MV_UP | MV_DOWN)    ? ScreenGfxPos : 0);
245       }
246
247       if (setup.soft_scrolling ||
248           ABS(ScreenMovPos) + ScrollStepSize == TILEX ||
249           ABS(ScreenMovPos) == ScrollStepSize ||
250           redraw_tiles > REDRAWTILES_THRESHOLD)
251       {
252         BlitBitmap(buffer, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
253
254 #ifdef DEBUG
255 #if 0
256         printf("redrawing all (ScreenGfxPos == %d) because %s\n",
257                ScreenGfxPos,
258                (setup.soft_scrolling ?
259                 "setup.soft_scrolling" :
260                 ABS(ScreenGfxPos) + ScrollStepSize == TILEX ?
261                 "ABS(ScreenGfxPos) + ScrollStepSize == TILEX" :
262                 ABS(ScreenGfxPos) == ScrollStepSize ?
263                 "ABS(ScreenGfxPos) == ScrollStepSize" :
264                 "redraw_tiles > REDRAWTILES_THRESHOLD"));
265 #endif
266 #endif
267       }
268     }
269
270     redraw_mask &= ~REDRAW_MAIN;
271   }
272
273   if (redraw_mask & REDRAW_DOORS)
274   {
275     if (redraw_mask & REDRAW_DOOR_1)
276       BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
277
278     if (redraw_mask & REDRAW_DOOR_2)
279     {
280 #if 0
281       if ((redraw_mask & REDRAW_DOOR_2) == REDRAW_DOOR_2)
282 #endif
283         BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
284 #if 0
285       else
286       {
287         if (redraw_mask & REDRAW_VIDEO_1)
288           BlitBitmap(backbuffer, window,
289                      VX + VIDEO_DISPLAY1_XPOS, VY + VIDEO_DISPLAY1_YPOS,
290                      VIDEO_DISPLAY_XSIZE, VIDEO_DISPLAY_YSIZE,
291                      VX + VIDEO_DISPLAY1_XPOS, VY + VIDEO_DISPLAY1_YPOS);
292         if (redraw_mask & REDRAW_VIDEO_2)
293           BlitBitmap(backbuffer, window,
294                      VX + VIDEO_DISPLAY2_XPOS, VY + VIDEO_DISPLAY2_YPOS,
295                      VIDEO_DISPLAY_XSIZE, VIDEO_DISPLAY_YSIZE,
296                      VX + VIDEO_DISPLAY2_XPOS, VY + VIDEO_DISPLAY2_YPOS);
297         if (redraw_mask & REDRAW_VIDEO_3)
298           BlitBitmap(backbuffer, window,
299                      VX + VIDEO_CONTROL_XPOS, VY + VIDEO_CONTROL_YPOS,
300                      VIDEO_CONTROL_XSIZE, VIDEO_CONTROL_YSIZE,
301                      VX + VIDEO_CONTROL_XPOS, VY + VIDEO_CONTROL_YPOS);
302       }
303 #endif
304     }
305
306     if (redraw_mask & REDRAW_DOOR_3)
307       BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
308
309     redraw_mask &= ~REDRAW_DOORS;
310   }
311
312   if (redraw_mask & REDRAW_MICROLEVEL)
313   {
314     BlitBitmap(backbuffer, window, SX, SY + 10 * TILEY, SXSIZE, 7 * TILEY,
315                SX, SY + 10 * TILEY);
316
317     redraw_mask &= ~REDRAW_MICROLEVEL;
318   }
319
320   if (redraw_mask & REDRAW_TILES)
321   {
322     for (x = 0; x < SCR_FIELDX; x++)
323       for (y = 0 ; y < SCR_FIELDY; y++)
324         if (redraw[redraw_x1 + x][redraw_y1 + y])
325           BlitBitmap(buffer, window,
326                      FX + x * TILEX, FY + y * TILEY, TILEX, TILEY,
327                      SX + x * TILEX, SY + y * TILEY);
328   }
329
330   if (redraw_mask & REDRAW_FPS)         /* display frames per second */
331   {
332     char text[100];
333     char info1[100];
334
335     sprintf(info1, " (only every %d. frame)", global.fps_slowdown_factor);
336     if (!global.fps_slowdown)
337       info1[0] = '\0';
338
339     sprintf(text, "%.1f fps%s", global.frames_per_second, info1);
340     DrawTextExt(window, SX, SY, text, FONT_TEXT_2, BLIT_OPAQUE);
341   }
342
343   FlushDisplay();
344
345   for (x = 0; x < MAX_BUF_XSIZE; x++)
346     for (y = 0; y < MAX_BUF_YSIZE; y++)
347       redraw[x][y] = 0;
348   redraw_tiles = 0;
349   redraw_mask = REDRAW_NONE;
350 }
351
352 void FadeToFront()
353 {
354 #if 0
355   long fading_delay = 300;
356
357   if (setup.fading && (redraw_mask & REDRAW_FIELD))
358   {
359 #endif
360
361 #if 0
362     int x,y;
363
364     ClearRectangle(window, REAL_SX,REAL_SY,FULL_SXSIZE,FULL_SYSIZE);
365     FlushDisplay();
366
367     for (i = 0; i < 2 * FULL_SYSIZE; i++)
368     {
369       for (y = 0; y < FULL_SYSIZE; y++)
370       {
371         BlitBitmap(backbuffer, window,
372                    REAL_SX,REAL_SY+i, FULL_SXSIZE,1, REAL_SX,REAL_SY+i);
373       }
374       FlushDisplay();
375       Delay(10);
376     }
377 #endif
378
379 #if 0
380     for (i = 1; i < FULL_SYSIZE; i+=2)
381       BlitBitmap(backbuffer, window,
382                  REAL_SX,REAL_SY+i, FULL_SXSIZE,1, REAL_SX,REAL_SY+i);
383     FlushDisplay();
384     Delay(fading_delay);
385 #endif
386
387 #if 0
388     SetClipOrigin(clip_gc[PIX_FADEMASK], 0, 0);
389     BlitBitmapMasked(backbuffer, window,
390                      REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
391                      REAL_SX,REAL_SY);
392     FlushDisplay();
393     Delay(fading_delay);
394
395     SetClipOrigin(clip_gc[PIX_FADEMASK], -1, -1);
396     BlitBitmapMasked(backbuffer, window,
397                      REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
398                      REAL_SX,REAL_SY);
399     FlushDisplay();
400     Delay(fading_delay);
401
402     SetClipOrigin(clip_gc[PIX_FADEMASK], 0, -1);
403     BlitBitmapMasked(backbuffer, window,
404                      REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
405                      REAL_SX,REAL_SY);
406     FlushDisplay();
407     Delay(fading_delay);
408
409     SetClipOrigin(clip_gc[PIX_FADEMASK], -1, 0);
410     BlitBitmapMasked(backbuffer, window,
411                      REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
412                      REAL_SX,REAL_SY);
413     FlushDisplay();
414     Delay(fading_delay);
415
416     redraw_mask &= ~REDRAW_MAIN;
417   }
418 #endif
419
420   BackToFront();
421 }
422
423 void SetMainBackgroundImage(int graphic)
424 {
425   SetMainBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
426                           graphic_info[graphic].bitmap ?
427                           graphic_info[graphic].bitmap :
428                           graphic_info[IMG_BACKGROUND].bitmap);
429 }
430
431 void SetDoorBackgroundImage(int graphic)
432 {
433   SetDoorBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
434                           graphic_info[graphic].bitmap ?
435                           graphic_info[graphic].bitmap :
436                           graphic_info[IMG_BACKGROUND].bitmap);
437 }
438
439 void DrawBackground(int dest_x, int dest_y, int width, int height)
440 {
441   ClearRectangleOnBackground(backbuffer, dest_x, dest_y, width, height);
442
443   redraw_mask |= REDRAW_FIELD;
444 }
445
446 void ClearWindow()
447 {
448   DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
449
450   if (setup.soft_scrolling && game_status == GAME_MODE_PLAYING)
451   {
452     ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
453     SetDrawtoField(DRAW_BUFFERED);
454   }
455   else
456     SetDrawtoField(DRAW_BACKBUFFER);
457
458   if (setup.direct_draw && game_status == GAME_MODE_PLAYING)
459   {
460     ClearRectangle(window, REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
461     SetDrawtoField(DRAW_DIRECT);
462   }
463 }
464
465 void MarkTileDirty(int x, int y)
466 {
467   int xx = redraw_x1 + x;
468   int yy = redraw_y1 + y;
469
470   if (!redraw[xx][yy])
471     redraw_tiles++;
472
473   redraw[xx][yy] = TRUE;
474   redraw_mask |= REDRAW_TILES;
475 }
476
477 void SetBorderElement()
478 {
479   int x, y;
480
481   BorderElement = EL_EMPTY;
482
483   for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
484   {
485     for (x = 0; x < lev_fieldx; x++)
486     {
487       if (!IS_INDESTRUCTIBLE(Feld[x][y]))
488         BorderElement = EL_STEELWALL;
489
490       if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
491         x = lev_fieldx - 2;
492     }
493   }
494 }
495
496 void SetRandomAnimationValue(int x, int y)
497 {
498   gfx.anim_random_frame = GfxRandom[x][y];
499 }
500
501 inline int getGraphicAnimationFrame(int graphic, int sync_frame)
502 {
503   /* animation synchronized with global frame counter, not move position */
504   if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
505     sync_frame = FrameCounter;
506
507   return getAnimationFrame(graphic_info[graphic].anim_frames,
508                            graphic_info[graphic].anim_delay,
509                            graphic_info[graphic].anim_mode,
510                            graphic_info[graphic].anim_start_frame,
511                            sync_frame);
512 }
513
514 inline void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
515                                     int graphic, int sync_frame, int mask_mode)
516 {
517   int frame = getGraphicAnimationFrame(graphic, sync_frame);
518
519   if (mask_mode == USE_MASKING)
520     DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
521   else
522     DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
523 }
524
525 inline void DrawGraphicAnimation(int x, int y, int graphic)
526 {
527   int lx = LEVELX(x), ly = LEVELY(y);
528
529   if (!IN_SCR_FIELD(x, y))
530     return;
531
532   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
533                           graphic, GfxFrame[lx][ly], NO_MASKING);
534   MarkTileDirty(x, y);
535 }
536
537 void DrawLevelGraphicAnimation(int x, int y, int graphic)
538 {
539   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
540 }
541
542 void DrawLevelElementAnimation(int x, int y, int element)
543 {
544 #if 1
545   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
546
547   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
548 #else
549   DrawGraphicAnimation(SCREENX(x), SCREENY(y), el2img(element));
550 #endif
551 }
552
553 inline void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
554 {
555   int sx = SCREENX(x), sy = SCREENY(y);
556
557   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
558     return;
559
560   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
561     return;
562
563   DrawGraphicAnimation(sx, sy, graphic);
564
565   if (GFX_CRUMBLED(Feld[x][y]))
566     DrawLevelFieldCrumbledSand(x, y);
567 }
568
569 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
570 {
571   int sx = SCREENX(x), sy = SCREENY(y);
572   int graphic;
573
574   if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
575     return;
576
577   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
578
579   if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
580     return;
581
582   DrawGraphicAnimation(sx, sy, graphic);
583
584   if (GFX_CRUMBLED(element))
585     DrawLevelFieldCrumbledSand(x, y);
586 }
587
588 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
589 {
590   if (player->use_murphy_graphic)
591   {
592     /* this works only because currently only one player can be "murphy" ... */
593     static int last_horizontal_dir = MV_LEFT;
594     int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
595
596     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
597       last_horizontal_dir = move_dir;
598
599     if (graphic == IMG_SP_MURPHY)       /* undefined => use special graphic */
600     {
601       int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
602
603       graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
604     }
605
606     return graphic;
607   }
608   else
609     return el_act_dir2img(player->element_nr, player->GfxAction, move_dir);
610 }
611
612 static boolean equalGraphics(int graphic1, int graphic2)
613 {
614   struct GraphicInfo *g1 = &graphic_info[graphic1];
615   struct GraphicInfo *g2 = &graphic_info[graphic2];
616
617   return (g1->bitmap      == g2->bitmap &&
618           g1->src_x       == g2->src_x &&
619           g1->src_y       == g2->src_y &&
620           g1->anim_frames == g2->anim_frames &&
621           g1->anim_delay  == g2->anim_delay &&
622           g1->anim_mode   == g2->anim_mode);
623 }
624
625 void DrawAllPlayers()
626 {
627   int i;
628
629   for (i = 0; i < MAX_PLAYERS; i++)
630     if (stored_player[i].active)
631       DrawPlayer(&stored_player[i]);
632 }
633
634 void DrawPlayerField(int x, int y)
635 {
636   if (!IS_PLAYER(x, y))
637     return;
638
639   DrawPlayer(PLAYERINFO(x, y));
640 }
641
642 void DrawPlayer(struct PlayerInfo *player)
643 {
644   int jx = player->jx;
645   int jy = player->jy;
646   int move_dir = player->MovDir;
647 #if 0
648   int last_jx = player->last_jx;
649   int last_jy = player->last_jy;
650   int next_jx = jx + (jx - last_jx);
651   int next_jy = jy + (jy - last_jy);
652   boolean player_is_moving = (last_jx != jx || last_jy != jy ? TRUE : FALSE);
653 #else
654   int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
655   int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? +1 : 0);
656   int last_jx = (player->is_moving ? jx - dx : jx);
657   int last_jy = (player->is_moving ? jy - dy : jy);
658   int next_jx = jx + dx;
659   int next_jy = jy + dy;
660   boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
661 #endif
662   int sx = SCREENX(jx), sy = SCREENY(jy);
663   int sxx = 0, syy = 0;
664   int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
665   int graphic;
666   int action = ACTION_DEFAULT;
667   int last_player_graphic = getPlayerGraphic(player, move_dir);
668   int last_player_frame = player->Frame;
669   int frame = 0;
670
671   if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
672     return;
673
674 #if DEBUG
675   if (!IN_LEV_FIELD(jx, jy))
676   {
677     printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
678     printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
679     printf("DrawPlayerField(): This should never happen!\n");
680     return;
681   }
682 #endif
683
684   if (element == EL_EXPLOSION)
685     return;
686
687   action = (player->is_pushing    ? ACTION_PUSHING         :
688             player->is_digging    ? ACTION_DIGGING         :
689             player->is_collecting ? ACTION_COLLECTING      :
690             player->is_moving     ? ACTION_MOVING          :
691             player->is_snapping   ? ACTION_SNAPPING        :
692             player->is_dropping   ? ACTION_DROPPING        :
693             player->is_waiting    ? player->action_waiting : ACTION_DEFAULT);
694
695   InitPlayerGfxAnimation(player, action, move_dir);
696
697   /* ----------------------------------------------------------------------- */
698   /* draw things in the field the player is leaving, if needed               */
699   /* ----------------------------------------------------------------------- */
700
701 #if 1
702   if (player->is_moving)
703 #else
704   if (player_is_moving)
705 #endif
706   {
707     if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
708     {
709       DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
710
711       if (last_element == EL_DYNAMITE_ACTIVE ||
712           last_element == EL_SP_DISK_RED_ACTIVE)
713         DrawDynamite(last_jx, last_jy);
714       else
715         DrawLevelFieldThruMask(last_jx, last_jy);
716     }
717     else if (last_element == EL_DYNAMITE_ACTIVE ||
718              last_element == EL_SP_DISK_RED_ACTIVE)
719       DrawDynamite(last_jx, last_jy);
720     else
721       DrawLevelField(last_jx, last_jy);
722
723     if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
724       DrawLevelElement(next_jx, next_jy, EL_EMPTY);
725   }
726
727   if (!IN_SCR_FIELD(sx, sy))
728     return;
729
730   if (setup.direct_draw)
731     SetDrawtoField(DRAW_BUFFERED);
732
733   /* ----------------------------------------------------------------------- */
734   /* draw things behind the player, if needed                                */
735   /* ----------------------------------------------------------------------- */
736
737   if (Back[jx][jy])
738     DrawLevelElement(jx, jy, Back[jx][jy]);
739   else if (IS_ACTIVE_BOMB(element))
740     DrawLevelElement(jx, jy, EL_EMPTY);
741   else
742   {
743     if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
744     {
745       if (GFX_CRUMBLED(GfxElement[jx][jy]))
746         DrawLevelFieldCrumbledSandDigging(jx, jy, move_dir, player->StepFrame);
747       else
748       {
749         int old_element = GfxElement[jx][jy];
750         int old_graphic = el_act_dir2img(old_element, action, move_dir);
751         int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
752
753         DrawGraphic(sx, sy, old_graphic, frame);
754       }
755     }
756     else
757     {
758       GfxElement[jx][jy] = EL_UNDEFINED;
759
760       DrawLevelField(jx, jy);
761     }
762   }
763
764   /* ----------------------------------------------------------------------- */
765   /* draw player himself                                                     */
766   /* ----------------------------------------------------------------------- */
767
768 #if 1
769
770   graphic = getPlayerGraphic(player, move_dir);
771
772   /* in the case of changed player action or direction, prevent the current
773      animation frame from being restarted for identical animations */
774   if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
775     player->Frame = last_player_frame;
776
777 #else
778
779   if (player->use_murphy_graphic)
780   {
781     static int last_horizontal_dir = MV_LEFT;
782
783     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
784       last_horizontal_dir = move_dir;
785
786     graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
787
788     if (graphic == IMG_SP_MURPHY)       /* undefined => use special graphic */
789     {
790       int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
791
792       graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
793     }
794   }
795   else
796     graphic = el_act_dir2img(player->element_nr, player->GfxAction, move_dir);
797
798 #endif
799
800   frame = getGraphicAnimationFrame(graphic, player->Frame);
801
802   if (player->GfxPos)
803   {
804     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
805       sxx = player->GfxPos;
806     else
807       syy = player->GfxPos;
808   }
809
810   if (!setup.soft_scrolling && ScreenMovPos)
811     sxx = syy = 0;
812
813   DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
814
815   if (SHIELD_ON(player))
816   {
817     int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
818                    IMG_SHIELD_NORMAL_ACTIVE);
819     int frame = getGraphicAnimationFrame(graphic, -1);
820
821     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
822   }
823
824   /* ----------------------------------------------------------------------- */
825   /* draw things the player is pushing, if needed                            */
826   /* ----------------------------------------------------------------------- */
827
828 #if 0
829   printf("::: %d, %d [%d, %d] [%d]\n",
830          player->is_pushing, player_is_moving, player->GfxAction,
831          player->is_moving, player_is_moving);
832 #endif
833
834 #if 1
835   if (player->is_pushing && player->is_moving)
836 #else
837   if (player->is_pushing && player_is_moving)
838 #endif
839   {
840     int px = SCREENX(next_jx), py = SCREENY(next_jy);
841
842     if (Back[next_jx][next_jy])
843       DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
844
845     if ((sxx || syy) && element == EL_SOKOBAN_OBJECT)
846       DrawGraphicShiftedThruMask(px, py, sxx, syy, IMG_SOKOBAN_OBJECT, 0,
847                                  NO_CUTTING);
848     else
849     {
850       int element = MovingOrBlocked2Element(next_jx, next_jy);
851       int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
852 #if 1
853       int frame = getGraphicAnimationFrame(graphic, player->StepFrame);
854 #else
855       int frame = getGraphicAnimationFrame(graphic, player->Frame);
856 #endif
857
858       DrawGraphicShifted(px, py, sxx, syy, graphic, frame,
859                          NO_CUTTING, NO_MASKING);
860     }
861   }
862
863   /* ----------------------------------------------------------------------- */
864   /* draw things in front of player (active dynamite or dynabombs)           */
865   /* ----------------------------------------------------------------------- */
866
867   if (IS_ACTIVE_BOMB(element))
868   {
869     graphic = el2img(element);
870     frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
871
872     if (game.emulation == EMU_SUPAPLEX)
873       DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
874     else
875       DrawGraphicThruMask(sx, sy, graphic, frame);
876   }
877
878   if (player_is_moving && last_element == EL_EXPLOSION)
879   {
880     int graphic = el_act2img(GfxElement[last_jx][last_jy], ACTION_EXPLODING);
881     int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
882     int phase = ExplodePhase[last_jx][last_jy] - 1;
883     int frame = getGraphicAnimationFrame(graphic, phase - delay);
884
885     if (phase >= delay)
886       DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
887   }
888
889   /* ----------------------------------------------------------------------- */
890   /* draw elements the player is just walking/passing through/under          */
891   /* ----------------------------------------------------------------------- */
892
893   /* handle the field the player is leaving ... */
894   if (player_is_moving && IS_ACCESSIBLE_INSIDE(last_element))
895     DrawLevelField(last_jx, last_jy);
896   else if (player_is_moving && IS_ACCESSIBLE_UNDER(last_element))
897     DrawLevelFieldThruMask(last_jx, last_jy);
898
899 #if 0
900   /* !!! I have forgotton what this should be good for !!! */
901   /* !!! causes player being visible when pushing from within tubes !!! */
902   if (!player->is_pushing)
903 #endif
904   {
905     /* ... and the field the player is entering */
906     if (IS_ACCESSIBLE_INSIDE(element))
907       DrawLevelField(jx, jy);
908     else if (IS_ACCESSIBLE_UNDER(element))
909       DrawLevelFieldThruMask(jx, jy);
910   }
911
912   if (setup.direct_draw)
913   {
914     int dest_x = SX + SCREENX(MIN(jx, last_jx)) * TILEX;
915     int dest_y = SY + SCREENY(MIN(jy, last_jy)) * TILEY;
916     int x_size = TILEX * (1 + ABS(jx - last_jx));
917     int y_size = TILEY * (1 + ABS(jy - last_jy));
918
919     BlitBitmap(drawto_field, window,
920                dest_x, dest_y, x_size, y_size, dest_x, dest_y);
921     SetDrawtoField(DRAW_DIRECT);
922   }
923
924   MarkTileDirty(sx, sy);
925 }
926
927 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
928 {
929   struct GraphicInfo *g = &graphic_info[graphic];
930
931   *bitmap = g->bitmap;
932
933   if (g->offset_y == 0)         /* frames are ordered horizontally */
934   {
935     int max_width = g->anim_frames_per_line * g->width;
936
937     *x = (g->src_x + frame * g->offset_x) % max_width;
938     *y = g->src_y + (g->src_x + frame * g->offset_x) / max_width * g->height;
939   }
940   else if (g->offset_x == 0)    /* frames are ordered vertically */
941   {
942     int max_height = g->anim_frames_per_line * g->height;
943
944     *x = g->src_x + (g->src_y + frame * g->offset_y) / max_height * g->width;
945     *y = (g->src_y + frame * g->offset_y) % max_height;
946   }
947   else                          /* frames are ordered diagonally */
948   {
949     *x = g->src_x + frame * g->offset_x;
950     *y = g->src_y + frame * g->offset_y;
951   }
952 }
953
954 void DrawGraphic(int x, int y, int graphic, int frame)
955 {
956 #if DEBUG
957   if (!IN_SCR_FIELD(x, y))
958   {
959     printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
960     printf("DrawGraphic(): This should never happen!\n");
961     return;
962   }
963 #endif
964
965   DrawGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic, frame);
966   MarkTileDirty(x, y);
967 }
968
969 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
970                     int frame)
971 {
972   Bitmap *src_bitmap;
973   int src_x, src_y;
974
975   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
976   BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
977 }
978
979 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
980 {
981 #if DEBUG
982   if (!IN_SCR_FIELD(x, y))
983   {
984     printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
985     printf("DrawGraphicThruMask(): This should never happen!\n");
986     return;
987   }
988 #endif
989
990   DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y *TILEY, graphic,
991                          frame);
992   MarkTileDirty(x, y);
993 }
994
995 void DrawGraphicThruMaskExt(DrawBuffer *d, int dest_x, int dest_y, int graphic,
996                             int frame)
997 {
998 #if 1
999   Bitmap *src_bitmap;
1000   int src_x, src_y;
1001   GC drawing_gc;
1002
1003   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1004   drawing_gc = src_bitmap->stored_clip_gc;
1005 #else
1006   GC drawing_gc = src_bitmap->stored_clip_gc;
1007   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
1008   int src_x = graphic_info[graphic].src_x;
1009   int src_y = graphic_info[graphic].src_y;
1010   int offset_x = graphic_info[graphic].offset_x;
1011   int offset_y = graphic_info[graphic].offset_y;
1012
1013   src_x += frame * offset_x;
1014   src_y += frame * offset_y;
1015
1016 #endif
1017
1018   SetClipOrigin(src_bitmap, drawing_gc, dest_x - src_x, dest_y - src_y);
1019   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY, dest_x, dest_y);
1020 }
1021
1022 void DrawMiniGraphic(int x, int y, int graphic)
1023 {
1024   DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1025   MarkTileDirty(x / 2, y / 2);
1026 }
1027
1028 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1029 {
1030   struct GraphicInfo *g = &graphic_info[graphic];
1031   int mini_startx = 0;
1032   int mini_starty = g->bitmap->height * 2 / 3;
1033
1034   *bitmap = g->bitmap;
1035   *x = mini_startx + g->src_x / 2;
1036   *y = mini_starty + g->src_y / 2;
1037 }
1038
1039 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1040 {
1041   Bitmap *src_bitmap;
1042   int src_x, src_y;
1043
1044   getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1045   BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1046 }
1047
1048 void DrawGraphicShifted(int x, int y, int dx, int dy, int graphic, int frame,
1049                         int cut_mode, int mask_mode)
1050 {
1051   Bitmap *src_bitmap;
1052   GC drawing_gc;
1053   int src_x, src_y;
1054   int width = TILEX, height = TILEY;
1055   int cx = 0, cy = 0;
1056   int dest_x, dest_y;
1057
1058   if (graphic < 0)
1059   {
1060     DrawGraphic(x, y, graphic, frame);
1061     return;
1062   }
1063
1064   if (dx || dy)                 /* shifted graphic */
1065   {
1066     if (x < BX1)                /* object enters playfield from the left */
1067     {
1068       x = BX1;
1069       width = dx;
1070       cx = TILEX - dx;
1071       dx = 0;
1072     }
1073     else if (x > BX2)           /* object enters playfield from the right */
1074     {
1075       x = BX2;
1076       width = -dx;
1077       dx = TILEX + dx;
1078     }
1079     else if (x==BX1 && dx < 0)  /* object leaves playfield to the left */
1080     {
1081       width += dx;
1082       cx = -dx;
1083       dx = 0;
1084     }
1085     else if (x==BX2 && dx > 0)  /* object leaves playfield to the right */
1086       width -= dx;
1087     else if (dx)                /* general horizontal movement */
1088       MarkTileDirty(x + SIGN(dx), y);
1089
1090     if (y < BY1)                /* object enters playfield from the top */
1091     {
1092       if (cut_mode==CUT_BELOW)  /* object completely above top border */
1093         return;
1094
1095       y = BY1;
1096       height = dy;
1097       cy = TILEY - dy;
1098       dy = 0;
1099     }
1100     else if (y > BY2)           /* object enters playfield from the bottom */
1101     {
1102       y = BY2;
1103       height = -dy;
1104       dy = TILEY + dy;
1105     }
1106     else if (y==BY1 && dy < 0)  /* object leaves playfield to the top */
1107     {
1108       height += dy;
1109       cy = -dy;
1110       dy = 0;
1111     }
1112     else if (dy > 0 && cut_mode == CUT_ABOVE)
1113     {
1114       if (y == BY2)             /* object completely above bottom border */
1115         return;
1116
1117       height = dy;
1118       cy = TILEY - dy;
1119       dy = TILEY;
1120       MarkTileDirty(x, y + 1);
1121     }                           /* object leaves playfield to the bottom */
1122     else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1123       height -= dy;
1124     else if (dy)                /* general vertical movement */
1125       MarkTileDirty(x, y + SIGN(dy));
1126   }
1127
1128 #if 1
1129   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1130 #else
1131   src_bitmap = graphic_info[graphic].bitmap;
1132   src_x = graphic_info[graphic].src_x;
1133   src_y = graphic_info[graphic].src_y;
1134   offset_x = graphic_info[graphic].offset_x;
1135   offset_y = graphic_info[graphic].offset_y;
1136
1137   src_x += frame * offset_x;
1138   src_y += frame * offset_y;
1139 #endif
1140
1141   drawing_gc = src_bitmap->stored_clip_gc;
1142
1143   src_x += cx;
1144   src_y += cy;
1145
1146   dest_x = FX + x * TILEX + dx;
1147   dest_y = FY + y * TILEY + dy;
1148
1149 #if DEBUG
1150   if (!IN_SCR_FIELD(x,y))
1151   {
1152     printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1153     printf("DrawGraphicShifted(): This should never happen!\n");
1154     return;
1155   }
1156 #endif
1157
1158   if (mask_mode == USE_MASKING)
1159   {
1160     SetClipOrigin(src_bitmap, drawing_gc, dest_x - src_x, dest_y - src_y);
1161     BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1162                      dest_x, dest_y);
1163   }
1164   else
1165     BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1166                dest_x, dest_y);
1167
1168   MarkTileDirty(x, y);
1169 }
1170
1171 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1172                                 int frame, int cut_mode)
1173 {
1174   DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1175 }
1176
1177 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1178                           int cut_mode, int mask_mode)
1179 {
1180   int lx = LEVELX(x), ly = LEVELY(y);
1181   int graphic;
1182   int frame;
1183
1184   if (IN_LEV_FIELD(lx, ly))
1185   {
1186     SetRandomAnimationValue(lx, ly);
1187
1188     graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1189     frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1190   }
1191   else  /* border element */
1192   {
1193     graphic = el2img(element);
1194     frame = getGraphicAnimationFrame(graphic, -1);
1195   }
1196
1197   if (element == EL_EXPANDABLE_WALL)
1198   {
1199     boolean left_stopped = FALSE, right_stopped = FALSE;
1200
1201     if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1202       left_stopped = TRUE;
1203     if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1204       right_stopped = TRUE;
1205
1206     if (left_stopped && right_stopped)
1207       graphic = IMG_WALL;
1208     else if (left_stopped)
1209     {
1210       graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1211       frame = graphic_info[graphic].anim_frames - 1;
1212     }
1213     else if (right_stopped)
1214     {
1215       graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1216       frame = graphic_info[graphic].anim_frames - 1;
1217     }
1218   }
1219
1220   if (dx || dy)
1221     DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1222   else if (mask_mode == USE_MASKING)
1223     DrawGraphicThruMask(x, y, graphic, frame);
1224   else
1225     DrawGraphic(x, y, graphic, frame);
1226 }
1227
1228 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1229                          int cut_mode, int mask_mode)
1230 {
1231   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1232     DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1233                          cut_mode, mask_mode);
1234 }
1235
1236 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1237                               int cut_mode)
1238 {
1239   DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1240 }
1241
1242 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1243                              int cut_mode)
1244 {
1245   DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1246 }
1247
1248 void DrawLevelElementThruMask(int x, int y, int element)
1249 {
1250   DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1251 }
1252
1253 void DrawLevelFieldThruMask(int x, int y)
1254 {
1255   DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1256 }
1257
1258 #define TILE_GFX_ELEMENT(x, y)                                              \
1259         (GfxElement[x][y] != EL_UNDEFINED && Feld[x][y] != EL_EXPLOSION ?   \
1260          GfxElement[x][y] : Feld[x][y])
1261
1262 static void DrawLevelFieldCrumbledSandExt(int x, int y, int graphic, int frame)
1263 {
1264   Bitmap *src_bitmap;
1265   int src_x, src_y;
1266   int sx = SCREENX(x), sy = SCREENY(y);
1267   int element;
1268   int width, height, cx, cy, i;
1269 #if 1
1270   int crumbled_border_size = graphic_info[graphic].border_size;
1271 #else
1272   int snip = TILEX / 8; /* number of border pixels from "crumbled graphic" */
1273 #endif
1274   static int xy[4][2] =
1275   {
1276     { 0, -1 },
1277     { -1, 0 },
1278     { +1, 0 },
1279     { 0, +1 }
1280   };
1281
1282 #if 0
1283   if (x == 0 && y == 7)
1284     printf("::: %d, %d [%d]\n", GfxElement[x][y], Feld[x][y],
1285            crumbled_border_size);
1286 #endif
1287
1288   if (!IN_LEV_FIELD(x, y))
1289     return;
1290
1291   element = TILE_GFX_ELEMENT(x, y);
1292
1293   /* crumble field itself */
1294   if (GFX_CRUMBLED(element) && !IS_MOVING(x, y))
1295   {
1296     if (!IN_SCR_FIELD(sx, sy))
1297       return;
1298
1299     getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1300
1301     for (i = 0; i < 4; i++)
1302     {
1303       int xx = x + xy[i][0];
1304       int yy = y + xy[i][1];
1305
1306 #if 1
1307       element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1308                  BorderElement);
1309 #else
1310       element = (IN_LEV_FIELD(xx, yy) ? Feld[xx][yy] : BorderElement);
1311 #endif
1312
1313       /* check if neighbour field is of same type */
1314       if (GFX_CRUMBLED(element) && !IS_MOVING(xx, yy))
1315         continue;
1316
1317 #if 0
1318       if (Feld[x][y] == EL_CUSTOM_START + 123)
1319         printf("::: crumble [%d] THE CHAOS ENGINE (%d, %d): %d, %d\n",
1320                i, Feld[x][y], element,
1321                GFX_CRUMBLED(element), IS_MOVING(x, y));
1322 #endif
1323
1324       if (i == 1 || i == 2)
1325       {
1326         width = crumbled_border_size;
1327         height = TILEY;
1328         cx = (i == 2 ? TILEX - crumbled_border_size : 0);
1329         cy = 0;
1330       }
1331       else
1332       {
1333         width = TILEX;
1334         height = crumbled_border_size;
1335         cx = 0;
1336         cy = (i == 3 ? TILEY - crumbled_border_size : 0);
1337       }
1338
1339       BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1340                  width, height, FX + sx * TILEX + cx, FY + sy * TILEY + cy);
1341     }
1342
1343     MarkTileDirty(sx, sy);
1344   }
1345   else          /* crumble neighbour fields */
1346   {
1347 #if 0
1348     getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1349 #endif
1350
1351     for (i = 0; i < 4; i++)
1352     {
1353       int xx = x + xy[i][0];
1354       int yy = y + xy[i][1];
1355       int sxx = sx + xy[i][0];
1356       int syy = sy + xy[i][1];
1357
1358 #if 1
1359       if (!IN_LEV_FIELD(xx, yy) ||
1360           !IN_SCR_FIELD(sxx, syy) ||
1361           IS_MOVING(xx, yy))
1362         continue;
1363
1364       element = TILE_GFX_ELEMENT(xx, yy);
1365
1366       if (!GFX_CRUMBLED(element))
1367         continue;
1368 #else
1369       if (!IN_LEV_FIELD(xx, yy) ||
1370           !IN_SCR_FIELD(sxx, syy) ||
1371           !GFX_CRUMBLED(Feld[xx][yy]) ||
1372           IS_MOVING(xx, yy))
1373         continue;
1374 #endif
1375
1376 #if 1
1377       graphic = el_act2crm(Feld[xx][yy], ACTION_DEFAULT);
1378       crumbled_border_size = graphic_info[graphic].border_size;
1379
1380       getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1381 #endif
1382
1383       if (i == 1 || i == 2)
1384       {
1385         width = crumbled_border_size;
1386         height = TILEY;
1387         cx = (i == 1 ? TILEX - crumbled_border_size : 0);
1388         cy = 0;
1389       }
1390       else
1391       {
1392         width = TILEX;
1393         height = crumbled_border_size;
1394         cx = 0;
1395         cy = (i == 0 ? TILEY - crumbled_border_size : 0);
1396       }
1397
1398       BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1399                  width, height, FX + sxx * TILEX + cx, FY + syy * TILEY + cy);
1400
1401       MarkTileDirty(sxx, syy);
1402     }
1403   }
1404 }
1405
1406 void DrawLevelFieldCrumbledSand(int x, int y)
1407 {
1408 #if 1
1409   int graphic;
1410
1411   if (!IN_LEV_FIELD(x, y))
1412     return;
1413
1414   graphic = el_act2crm(Feld[x][y], ACTION_DEFAULT);
1415
1416   DrawLevelFieldCrumbledSandExt(x, y, graphic, 0);
1417 #else
1418   DrawLevelFieldCrumbledSandExt(x, y, IMG_SAND_CRUMBLED, 0);
1419 #endif
1420 }
1421
1422 void DrawLevelFieldCrumbledSandDigging(int x, int y, int direction,
1423                                        int step_frame)
1424 {
1425 #if 1
1426   int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1427   int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1428 #else
1429   int graphic1 = el_act_dir2img(EL_SAND,          ACTION_DIGGING, direction);
1430   int graphic2 = el_act_dir2img(EL_SAND_CRUMBLED, ACTION_DIGGING, direction);
1431 #endif
1432   int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1433   int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1434   int sx = SCREENX(x), sy = SCREENY(y);
1435
1436   DrawGraphic(sx, sy, graphic1, frame1);
1437   DrawLevelFieldCrumbledSandExt(x, y, graphic2, frame2);
1438 }
1439
1440 void DrawLevelFieldCrumbledSandNeighbours(int x, int y)
1441 {
1442   int sx = SCREENX(x), sy = SCREENY(y);
1443   static int xy[4][2] =
1444   {
1445     { 0, -1 },
1446     { -1, 0 },
1447     { +1, 0 },
1448     { 0, +1 }
1449   };
1450   int i;
1451
1452   for (i = 0; i < 4; i++)
1453   {
1454     int xx = x + xy[i][0];
1455     int yy = y + xy[i][1];
1456     int sxx = sx + xy[i][0];
1457     int syy = sy + xy[i][1];
1458
1459     if (!IN_LEV_FIELD(xx, yy) ||
1460         !IN_SCR_FIELD(sxx, syy) ||
1461         !GFX_CRUMBLED(Feld[xx][yy]) ||
1462         IS_MOVING(xx, yy))
1463       continue;
1464
1465     DrawLevelField(xx, yy);
1466   }
1467 }
1468
1469 static int getBorderElement(int x, int y)
1470 {
1471   int border[7][2] =
1472   {
1473     { EL_STEELWALL_TOPLEFT,             EL_INVISIBLE_STEELWALL_TOPLEFT     },
1474     { EL_STEELWALL_TOPRIGHT,            EL_INVISIBLE_STEELWALL_TOPRIGHT    },
1475     { EL_STEELWALL_BOTTOMLEFT,          EL_INVISIBLE_STEELWALL_BOTTOMLEFT  },
1476     { EL_STEELWALL_BOTTOMRIGHT,         EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1477     { EL_STEELWALL_VERTICAL,            EL_INVISIBLE_STEELWALL_VERTICAL    },
1478     { EL_STEELWALL_HORIZONTAL,          EL_INVISIBLE_STEELWALL_HORIZONTAL  },
1479     { EL_STEELWALL,                     EL_INVISIBLE_STEELWALL             }
1480   };
1481   int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1482   int steel_position = (x == -1         && y == -1              ? 0 :
1483                         x == lev_fieldx && y == -1              ? 1 :
1484                         x == -1         && y == lev_fieldy      ? 2 :
1485                         x == lev_fieldx && y == lev_fieldy      ? 3 :
1486                         x == -1         || x == lev_fieldx      ? 4 :
1487                         y == -1         || y == lev_fieldy      ? 5 : 6);
1488
1489   return border[steel_position][steel_type];
1490 }
1491
1492 void DrawScreenElement(int x, int y, int element)
1493 {
1494   DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1495   DrawLevelFieldCrumbledSand(LEVELX(x), LEVELY(y));
1496 }
1497
1498 void DrawLevelElement(int x, int y, int element)
1499 {
1500   if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1501     DrawScreenElement(SCREENX(x), SCREENY(y), element);
1502 }
1503
1504 void DrawScreenField(int x, int y)
1505 {
1506   int lx = LEVELX(x), ly = LEVELY(y);
1507   int element, content;
1508
1509   if (!IN_LEV_FIELD(lx, ly))
1510   {
1511     if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1512       element = EL_EMPTY;
1513     else
1514       element = getBorderElement(lx, ly);
1515
1516     DrawScreenElement(x, y, element);
1517     return;
1518   }
1519
1520   element = Feld[lx][ly];
1521   content = Store[lx][ly];
1522
1523   if (IS_MOVING(lx, ly))
1524   {
1525     int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1526     boolean cut_mode = NO_CUTTING;
1527
1528     if (element == EL_QUICKSAND_EMPTYING ||
1529         element == EL_MAGIC_WALL_EMPTYING ||
1530         element == EL_BD_MAGIC_WALL_EMPTYING ||
1531         element == EL_AMOEBA_DROPPING)
1532       cut_mode = CUT_ABOVE;
1533     else if (element == EL_QUICKSAND_FILLING ||
1534              element == EL_MAGIC_WALL_FILLING ||
1535              element == EL_BD_MAGIC_WALL_FILLING)
1536       cut_mode = CUT_BELOW;
1537
1538     if (cut_mode == CUT_ABOVE)
1539       DrawScreenElementShifted(x, y, 0, 0, element, NO_CUTTING);
1540     else
1541       DrawScreenElement(x, y, EL_EMPTY);
1542
1543     if (horiz_move)
1544       DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1545     else if (cut_mode == NO_CUTTING)
1546       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1547     else
1548       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1549
1550     if (content == EL_ACID)
1551     {
1552       int dir = MovDir[lx][ly];
1553       int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
1554       int newly = ly + (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
1555
1556       DrawLevelElementThruMask(newlx, newly, EL_ACID);
1557     }
1558   }
1559   else if (IS_BLOCKED(lx, ly))
1560   {
1561     int oldx, oldy;
1562     int sx, sy;
1563     int horiz_move;
1564     boolean cut_mode = NO_CUTTING;
1565     int element_old, content_old;
1566
1567     Blocked2Moving(lx, ly, &oldx, &oldy);
1568     sx = SCREENX(oldx);
1569     sy = SCREENY(oldy);
1570     horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
1571                   MovDir[oldx][oldy] == MV_RIGHT);
1572
1573     element_old = Feld[oldx][oldy];
1574     content_old = Store[oldx][oldy];
1575
1576     if (element_old == EL_QUICKSAND_EMPTYING ||
1577         element_old == EL_MAGIC_WALL_EMPTYING ||
1578         element_old == EL_BD_MAGIC_WALL_EMPTYING ||
1579         element_old == EL_AMOEBA_DROPPING)
1580       cut_mode = CUT_ABOVE;
1581
1582     DrawScreenElement(x, y, EL_EMPTY);
1583
1584     if (horiz_move)
1585       DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
1586                                NO_CUTTING);
1587     else if (cut_mode == NO_CUTTING)
1588       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
1589                                cut_mode);
1590     else
1591       DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
1592                                cut_mode);
1593   }
1594   else if (IS_DRAWABLE(element))
1595     DrawScreenElement(x, y, element);
1596   else
1597     DrawScreenElement(x, y, EL_EMPTY);
1598 }
1599
1600 void DrawLevelField(int x, int y)
1601 {
1602   if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1603     DrawScreenField(SCREENX(x), SCREENY(y));
1604   else if (IS_MOVING(x, y))
1605   {
1606     int newx,newy;
1607
1608     Moving2Blocked(x, y, &newx, &newy);
1609     if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
1610       DrawScreenField(SCREENX(newx), SCREENY(newy));
1611   }
1612   else if (IS_BLOCKED(x, y))
1613   {
1614     int oldx, oldy;
1615
1616     Blocked2Moving(x, y, &oldx, &oldy);
1617     if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
1618       DrawScreenField(SCREENX(oldx), SCREENY(oldy));
1619   }
1620 }
1621
1622 void DrawMiniElement(int x, int y, int element)
1623 {
1624   int graphic;
1625
1626   graphic = el2edimg(element);
1627   DrawMiniGraphic(x, y, graphic);
1628 }
1629
1630 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
1631 {
1632   int x = sx + scroll_x, y = sy + scroll_y;
1633
1634   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
1635     DrawMiniElement(sx, sy, EL_EMPTY);
1636   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
1637     DrawMiniElement(sx, sy, Feld[x][y]);
1638   else
1639     DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
1640 }
1641
1642 void DrawEnvelopeBackground(int envelope_nr, int startx, int starty,
1643                             int x, int y, int xsize, int ysize, int font_nr)
1644 {
1645   int font_width  = getFontWidth(font_nr);
1646   int font_height = getFontHeight(font_nr);
1647   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
1648   Bitmap *src_bitmap;
1649   int src_x, src_y;
1650   int dst_x = SX + startx + x * font_width;
1651   int dst_y = SY + starty + y * font_height;
1652   int width  = graphic_info[graphic].width;
1653   int height = graphic_info[graphic].height;
1654   int inner_width  = MAX(width  - 2 * font_width,  font_width);
1655   int inner_height = MAX(height - 2 * font_height, font_height);
1656   int inner_sx = (width >= 3 * font_width ? font_width : 0);
1657   int inner_sy = (height >= 3 * font_height ? font_height : 0);
1658   boolean draw_masked = graphic_info[graphic].draw_masked;
1659
1660   getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1661
1662   if (src_bitmap == NULL || width < font_width || height < font_height)
1663   {
1664     ClearRectangle(drawto, dst_x, dst_y, font_width, font_height);
1665     return;
1666   }
1667
1668   src_x += (x == 0 ? 0 : x == xsize - 1 ? width  - font_width  :
1669             inner_sx + (x - 1) * font_width  % inner_width);
1670   src_y += (y == 0 ? 0 : y == ysize - 1 ? height - font_height :
1671             inner_sy + (y - 1) * font_height % inner_height);
1672
1673   if (draw_masked)
1674   {
1675     SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
1676                   dst_x - src_x, dst_y - src_y);
1677     BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, font_width, font_height,
1678                      dst_x, dst_y);
1679   }
1680   else
1681     BlitBitmap(src_bitmap, drawto, src_x, src_y, font_width, font_height,
1682                dst_x, dst_y);
1683 }
1684
1685 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
1686 {
1687   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
1688   boolean draw_masked = graphic_info[graphic].draw_masked;
1689   int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_ON_BACKGROUND);
1690   boolean ffwd_delay = (tape.playing && tape.fast_forward);
1691   boolean no_delay = (tape.warp_forward);
1692   unsigned long anim_delay = 0;
1693   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
1694   int anim_delay_value = (no_delay ? 0 : frame_delay_value);
1695   int font_nr = FONT_ENVELOPE_1 + envelope_nr;
1696   int font_width = getFontWidth(font_nr);
1697   int font_height = getFontHeight(font_nr);
1698   int max_xsize = level.envelope_xsize[envelope_nr];
1699   int max_ysize = level.envelope_ysize[envelope_nr];
1700   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
1701   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
1702   int xend = max_xsize;
1703   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
1704   int xstep = (xstart < xend ? 1 : 0);
1705   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
1706   int x, y;
1707
1708   for (x = xstart, y = ystart; x <= xend && y <= yend; x += xstep, y += ystep)
1709   {
1710     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
1711     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
1712     int sx = (SXSIZE - xsize * font_width)  / 2;
1713     int sy = (SYSIZE - ysize * font_height) / 2;
1714     int xx, yy;
1715
1716     SetDrawtoField(DRAW_BUFFERED);
1717
1718     BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1719
1720     SetDrawtoField(DRAW_BACKBUFFER);
1721
1722     for (yy = 0; yy < ysize; yy++) for (xx = 0; xx < xsize; xx++)
1723       DrawEnvelopeBackground(envelope_nr, sx,sy, xx,yy, xsize, ysize, font_nr);
1724
1725     DrawTextToTextArea(SX + sx + font_width, SY + sy + font_height,
1726                        level.envelope_text[envelope_nr], font_nr, max_xsize,
1727                        xsize - 2, ysize - 2, mask_mode);
1728
1729     redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
1730     BackToFront();
1731
1732     WaitUntilDelayReached(&anim_delay, anim_delay_value / 2);
1733   }
1734 }
1735
1736 void ShowEnvelope(int envelope_nr)
1737 {
1738   int element = EL_ENVELOPE_1 + envelope_nr;
1739   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
1740   int sound_opening = element_info[element].sound[ACTION_OPENING];
1741   int sound_closing = element_info[element].sound[ACTION_CLOSING];
1742   boolean ffwd_delay = (tape.playing && tape.fast_forward);
1743   boolean no_delay = (tape.warp_forward);
1744   int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
1745   int wait_delay_value = (no_delay ? 0 : normal_delay_value);
1746   int anim_mode = graphic_info[graphic].anim_mode;
1747   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
1748                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
1749
1750   game.envelope_active = TRUE;  /* needed for RedrawPlayfield() events */
1751
1752   PlaySoundStereo(sound_opening, SOUND_MIDDLE);
1753
1754   if (anim_mode == ANIM_DEFAULT)
1755     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
1756
1757   AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
1758
1759   if (tape.playing)
1760     Delay(wait_delay_value);
1761   else
1762     WaitForEventToContinue();
1763
1764   PlaySoundStereo(sound_closing, SOUND_MIDDLE);
1765
1766   if (anim_mode != ANIM_NONE)
1767     AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
1768
1769   if (anim_mode == ANIM_DEFAULT)
1770     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
1771
1772   game.envelope_active = FALSE;
1773
1774   SetDrawtoField(DRAW_BUFFERED);
1775
1776   redraw_mask |= REDRAW_FIELD;
1777   BackToFront();
1778 }
1779
1780 void getMicroGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1781 {
1782   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
1783   int mini_startx = src_bitmap->width * 3 / 4;
1784   int mini_starty = src_bitmap->height * 2 / 3;
1785   int src_x = mini_startx + graphic_info[graphic].src_x / 8;
1786   int src_y = mini_starty + graphic_info[graphic].src_y / 8;
1787
1788   *bitmap = src_bitmap;
1789   *x = src_x;
1790   *y = src_y;
1791 }
1792
1793 void DrawMicroElement(int xpos, int ypos, int element)
1794 {
1795   Bitmap *src_bitmap;
1796   int src_x, src_y;
1797   int graphic = el2preimg(element);
1798
1799   getMicroGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1800   BlitBitmap(src_bitmap, drawto, src_x, src_y, MICRO_TILEX, MICRO_TILEY,
1801              xpos, ypos);
1802 }
1803
1804 void DrawLevel()
1805 {
1806   int x,y;
1807
1808   SetDrawBackgroundMask(REDRAW_NONE);
1809   ClearWindow();
1810
1811   for (x = BX1; x <= BX2; x++)
1812     for (y = BY1; y <= BY2; y++)
1813       DrawScreenField(x, y);
1814
1815   redraw_mask |= REDRAW_FIELD;
1816 }
1817
1818 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
1819 {
1820   int x,y;
1821
1822   for (x = 0; x < size_x; x++)
1823     for (y = 0; y < size_y; y++)
1824       DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
1825
1826   redraw_mask |= REDRAW_FIELD;
1827 }
1828
1829 static void DrawMicroLevelExt(int xpos, int ypos, int from_x, int from_y)
1830 {
1831   int x, y;
1832
1833   DrawBackground(xpos, ypos, MICROLEV_XSIZE, MICROLEV_YSIZE);
1834
1835   if (lev_fieldx < STD_LEV_FIELDX)
1836     xpos += (STD_LEV_FIELDX - lev_fieldx) / 2 * MICRO_TILEX;
1837   if (lev_fieldy < STD_LEV_FIELDY)
1838     ypos += (STD_LEV_FIELDY - lev_fieldy) / 2 * MICRO_TILEY;
1839
1840   xpos += MICRO_TILEX;
1841   ypos += MICRO_TILEY;
1842
1843   for (x = -1; x <= STD_LEV_FIELDX; x++)
1844   {
1845     for (y = -1; y <= STD_LEV_FIELDY; y++)
1846     {
1847       int lx = from_x + x, ly = from_y + y;
1848
1849       if (lx >= 0 && lx < lev_fieldx && ly >= 0 && ly < lev_fieldy)
1850         DrawMicroElement(xpos + x * MICRO_TILEX, ypos + y * MICRO_TILEY,
1851                          level.field[lx][ly]);
1852       else if (lx >= -1 && lx < lev_fieldx+1 && ly >= -1 && ly < lev_fieldy+1
1853                && BorderElement != EL_EMPTY)
1854         DrawMicroElement(xpos + x * MICRO_TILEX, ypos + y * MICRO_TILEY,
1855                          getBorderElement(lx, ly));
1856     }
1857   }
1858
1859   redraw_mask |= REDRAW_MICROLEVEL;
1860 }
1861
1862 #define MICROLABEL_EMPTY                0
1863 #define MICROLABEL_LEVEL_NAME           1
1864 #define MICROLABEL_CREATED_BY           2
1865 #define MICROLABEL_LEVEL_AUTHOR         3
1866 #define MICROLABEL_IMPORTED_FROM        4
1867 #define MICROLABEL_LEVEL_IMPORT_INFO    5
1868
1869 static void DrawMicroLevelLabelExt(int mode)
1870 {
1871   char label_text[MAX_OUTPUT_LINESIZE + 1];
1872   int max_len_label_text;
1873   int font_nr = FONT_TEXT_2;
1874
1875   if (mode == MICROLABEL_CREATED_BY || mode == MICROLABEL_IMPORTED_FROM)
1876     font_nr = FONT_TEXT_3;
1877
1878   max_len_label_text = SXSIZE / getFontWidth(font_nr);
1879
1880   DrawBackground(SX, MICROLABEL_YPOS, SXSIZE, getFontHeight(font_nr));
1881
1882   strncpy(label_text, (mode == MICROLABEL_LEVEL_NAME ? level.name :
1883                        mode == MICROLABEL_CREATED_BY ? "created by" :
1884                        mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
1885                        mode == MICROLABEL_IMPORTED_FROM ? "imported from" :
1886                        mode == MICROLABEL_LEVEL_IMPORT_INFO ?
1887                        leveldir_current->imported_from : ""),
1888           max_len_label_text);
1889   label_text[max_len_label_text] = '\0';
1890
1891   if (strlen(label_text) > 0)
1892   {
1893     int lxpos = SX + (SXSIZE - getTextWidth(label_text, font_nr)) / 2;
1894     int lypos = MICROLABEL_YPOS;
1895
1896     DrawText(lxpos, lypos, label_text, font_nr);
1897   }
1898
1899   redraw_mask |= REDRAW_MICROLEVEL;
1900 }
1901
1902 void DrawMicroLevel(int xpos, int ypos, boolean restart)
1903 {
1904   static unsigned long scroll_delay = 0;
1905   static unsigned long label_delay = 0;
1906   static int from_x, from_y, scroll_direction;
1907   static int label_state, label_counter;
1908   int last_game_status = game_status;   /* save current game status */
1909
1910   /* force PREVIEW font on preview level */
1911   game_status = GAME_MODE_PSEUDO_PREVIEW;
1912
1913   if (restart)
1914   {
1915     from_x = from_y = 0;
1916     scroll_direction = MV_RIGHT;
1917     label_state = 1;
1918     label_counter = 0;
1919
1920     DrawMicroLevelExt(xpos, ypos, from_x, from_y);
1921     DrawMicroLevelLabelExt(label_state);
1922
1923     /* initialize delay counters */
1924     DelayReached(&scroll_delay, 0);
1925     DelayReached(&label_delay, 0);
1926
1927     if (leveldir_current->name)
1928     {
1929       int text_width = getTextWidth(leveldir_current->name, FONT_TEXT_1);
1930       int lxpos = SX + (SXSIZE - text_width) / 2;
1931       int lypos = SY + 352;
1932
1933       DrawText(lxpos, lypos, leveldir_current->name, FONT_TEXT_1);
1934     }
1935
1936     game_status = last_game_status;     /* restore current game status */
1937
1938     return;
1939   }
1940
1941   /* scroll micro level, if needed */
1942   if ((lev_fieldx > STD_LEV_FIELDX || lev_fieldy > STD_LEV_FIELDY) &&
1943       DelayReached(&scroll_delay, MICROLEVEL_SCROLL_DELAY))
1944   {
1945     switch (scroll_direction)
1946     {
1947       case MV_LEFT:
1948         if (from_x > 0)
1949           from_x--;
1950         else
1951           scroll_direction = MV_UP;
1952         break;
1953
1954       case MV_RIGHT:
1955         if (from_x < lev_fieldx - STD_LEV_FIELDX)
1956           from_x++;
1957         else
1958           scroll_direction = MV_DOWN;
1959         break;
1960
1961       case MV_UP:
1962         if (from_y > 0)
1963           from_y--;
1964         else
1965           scroll_direction = MV_RIGHT;
1966         break;
1967
1968       case MV_DOWN:
1969         if (from_y < lev_fieldy - STD_LEV_FIELDY)
1970           from_y++;
1971         else
1972           scroll_direction = MV_LEFT;
1973         break;
1974
1975       default:
1976         break;
1977     }
1978
1979     DrawMicroLevelExt(xpos, ypos, from_x, from_y);
1980   }
1981
1982   /* redraw micro level label, if needed */
1983   if (strcmp(level.name, NAMELESS_LEVEL_NAME) != 0 &&
1984       strcmp(level.author, ANONYMOUS_NAME) != 0 &&
1985       strcmp(level.author, leveldir_current->name) != 0 &&
1986       DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
1987   {
1988     int max_label_counter = 23;
1989
1990     if (leveldir_current->imported_from != NULL)
1991       max_label_counter += 14;
1992
1993     label_counter = (label_counter + 1) % max_label_counter;
1994     label_state = (label_counter >= 0 && label_counter <= 7 ?
1995                    MICROLABEL_LEVEL_NAME :
1996                    label_counter >= 9 && label_counter <= 12 ?
1997                    MICROLABEL_CREATED_BY :
1998                    label_counter >= 14 && label_counter <= 21 ?
1999                    MICROLABEL_LEVEL_AUTHOR :
2000                    label_counter >= 23 && label_counter <= 26 ?
2001                    MICROLABEL_IMPORTED_FROM :
2002                    label_counter >= 28 && label_counter <= 35 ?
2003                    MICROLABEL_LEVEL_IMPORT_INFO : MICROLABEL_EMPTY);
2004     DrawMicroLevelLabelExt(label_state);
2005   }
2006
2007   game_status = last_game_status;       /* restore current game status */
2008 }
2009
2010 void WaitForEventToContinue()
2011 {
2012   boolean still_wait = TRUE;
2013
2014   /* simulate releasing mouse button over last gadget, if still pressed */
2015   if (button_status)
2016     HandleGadgets(-1, -1, 0);
2017
2018   button_status = MB_RELEASED;
2019
2020   while (still_wait)
2021   {
2022     if (PendingEvent())
2023     {
2024       Event event;
2025
2026       NextEvent(&event);
2027
2028       switch (event.type)
2029       {
2030         case EVENT_BUTTONPRESS:
2031         case EVENT_KEYPRESS:
2032           still_wait = FALSE;
2033           break;
2034
2035         case EVENT_KEYRELEASE:
2036           ClearPlayerAction();
2037           break;
2038
2039         default:
2040           HandleOtherEvents(&event);
2041           break;
2042       }
2043     }
2044     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
2045     {
2046       still_wait = FALSE;
2047     }
2048
2049     DoAnimation();
2050
2051     /* don't eat all CPU time */
2052     Delay(10);
2053   }
2054 }
2055
2056 #define MAX_REQUEST_LINES               13
2057 #define MAX_REQUEST_LINE_FONT1_LEN      7
2058 #define MAX_REQUEST_LINE_FONT2_LEN      10
2059
2060 boolean Request(char *text, unsigned int req_state)
2061 {
2062   int mx, my, ty, result = -1;
2063   unsigned int old_door_state;
2064   int last_game_status = game_status;   /* save current game status */
2065   int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
2066   int font_nr = FONT_TEXT_2;
2067   int max_word_len = 0;
2068   char *text_ptr;
2069
2070   for (text_ptr = text; *text_ptr; text_ptr++)
2071   {
2072     max_word_len = (*text_ptr != ' ' ? max_word_len + 1 : 0);
2073
2074     if (max_word_len > MAX_REQUEST_LINE_FONT1_LEN)
2075     {
2076       max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
2077       font_nr = FONT_LEVEL_NUMBER;
2078
2079       break;
2080     }
2081   }
2082
2083 #if 1
2084   /* disable deactivated drawing when quick-loading level tape recording */
2085   if (tape.playing && tape.deactivate_display)
2086     TapeDeactivateDisplayOff(TRUE);
2087 #endif
2088
2089 #if 1
2090   SetMouseCursor(CURSOR_DEFAULT);
2091 #endif
2092
2093 #if defined(NETWORK_AVALIABLE)
2094   /* pause network game while waiting for request to answer */
2095   if (options.network &&
2096       game_status == GAME_MODE_PLAYING &&
2097       req_state & REQUEST_WAIT_FOR)
2098     SendToServer_PausePlaying();
2099 #endif
2100
2101   old_door_state = GetDoorState();
2102
2103   /* simulate releasing mouse button over last gadget, if still pressed */
2104   if (button_status)
2105     HandleGadgets(-1, -1, 0);
2106
2107   UnmapAllGadgets();
2108
2109   CloseDoor(DOOR_CLOSE_1);
2110
2111   /* save old door content */
2112   BlitBitmap(bitmap_db_door, bitmap_db_door,
2113              DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE,
2114              DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1);
2115
2116   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
2117
2118   /* clear door drawing field */
2119   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2120
2121   /* force DOOR font on preview level */
2122   game_status = GAME_MODE_PSEUDO_DOOR;
2123
2124   /* write text for request */
2125   for (ty = 0; ty < MAX_REQUEST_LINES; ty++)
2126   {
2127     char text_line[max_request_line_len + 1];
2128     int tx, tl, tc = 0;
2129
2130     if (!*text)
2131       break;
2132
2133     for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
2134     {
2135       tc = *(text + tx);
2136       if (!tc || tc == ' ')
2137         break;
2138     }
2139
2140     if (!tl)
2141     { 
2142       text++; 
2143       ty--; 
2144       continue; 
2145     }
2146
2147     strncpy(text_line, text, tl);
2148     text_line[tl] = 0;
2149
2150     DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
2151              DY + 8 + ty * (getFontHeight(font_nr) + 2),
2152              text_line, font_nr);
2153
2154     text += tl + (tc == ' ' ? 1 : 0);
2155   }
2156
2157   game_status = last_game_status;       /* restore current game status */
2158
2159   if (req_state & REQ_ASK)
2160   {
2161     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2162     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2163   }
2164   else if (req_state & REQ_CONFIRM)
2165   {
2166     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2167   }
2168   else if (req_state & REQ_PLAYER)
2169   {
2170     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2171     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2172     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2173     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2174   }
2175
2176   /* copy request gadgets to door backbuffer */
2177   BlitBitmap(drawto, bitmap_db_door,
2178              DX, DY, DXSIZE, DYSIZE,
2179              DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2180
2181   OpenDoor(DOOR_OPEN_1);
2182
2183 #if 0
2184   ClearEventQueue();
2185 #endif
2186
2187   if (!(req_state & REQUEST_WAIT_FOR))
2188   {
2189     SetDrawBackgroundMask(REDRAW_FIELD);
2190
2191     return FALSE;
2192   }
2193
2194   if (game_status != GAME_MODE_MAIN)
2195     InitAnimation();
2196
2197   button_status = MB_RELEASED;
2198
2199   request_gadget_id = -1;
2200
2201   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
2202
2203 #if 0
2204   SetMouseCursor(CURSOR_DEFAULT);
2205 #endif
2206
2207   while (result < 0)
2208   {
2209     if (PendingEvent())
2210     {
2211       Event event;
2212
2213       NextEvent(&event);
2214
2215       switch(event.type)
2216       {
2217         case EVENT_BUTTONPRESS:
2218         case EVENT_BUTTONRELEASE:
2219         case EVENT_MOTIONNOTIFY:
2220         {
2221           if (event.type == EVENT_MOTIONNOTIFY)
2222           {
2223             if (!PointerInWindow(window))
2224               continue; /* window and pointer are on different screens */
2225
2226             if (!button_status)
2227               continue;
2228
2229             motion_status = TRUE;
2230             mx = ((MotionEvent *) &event)->x;
2231             my = ((MotionEvent *) &event)->y;
2232           }
2233           else
2234           {
2235             motion_status = FALSE;
2236             mx = ((ButtonEvent *) &event)->x;
2237             my = ((ButtonEvent *) &event)->y;
2238             if (event.type == EVENT_BUTTONPRESS)
2239               button_status = ((ButtonEvent *) &event)->button;
2240             else
2241               button_status = MB_RELEASED;
2242           }
2243
2244           /* this sets 'request_gadget_id' */
2245           HandleGadgets(mx, my, button_status);
2246
2247           switch(request_gadget_id)
2248           {
2249             case TOOL_CTRL_ID_YES:
2250               result = TRUE;
2251               break;
2252             case TOOL_CTRL_ID_NO:
2253               result = FALSE;
2254               break;
2255             case TOOL_CTRL_ID_CONFIRM:
2256               result = TRUE | FALSE;
2257               break;
2258
2259             case TOOL_CTRL_ID_PLAYER_1:
2260               result = 1;
2261               break;
2262             case TOOL_CTRL_ID_PLAYER_2:
2263               result = 2;
2264               break;
2265             case TOOL_CTRL_ID_PLAYER_3:
2266               result = 3;
2267               break;
2268             case TOOL_CTRL_ID_PLAYER_4:
2269               result = 4;
2270               break;
2271
2272             default:
2273               break;
2274           }
2275
2276           break;
2277         }
2278
2279         case EVENT_KEYPRESS:
2280           switch(GetEventKey((KeyEvent *)&event, TRUE))
2281           {
2282             case KSYM_Return:
2283               result = 1;
2284               break;
2285
2286             case KSYM_Escape:
2287               result = 0;
2288               break;
2289
2290             default:
2291               break;
2292           }
2293           if (req_state & REQ_PLAYER)
2294             result = 0;
2295           break;
2296
2297         case EVENT_KEYRELEASE:
2298           ClearPlayerAction();
2299           break;
2300
2301         default:
2302           HandleOtherEvents(&event);
2303           break;
2304       }
2305     }
2306     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
2307     {
2308       int joy = AnyJoystick();
2309
2310       if (joy & JOY_BUTTON_1)
2311         result = 1;
2312       else if (joy & JOY_BUTTON_2)
2313         result = 0;
2314     }
2315
2316     DoAnimation();
2317
2318     /* don't eat all CPU time */
2319     Delay(10);
2320   }
2321
2322   if (game_status != GAME_MODE_MAIN)
2323     StopAnimation();
2324
2325   UnmapToolButtons();
2326
2327   if (!(req_state & REQ_STAY_OPEN))
2328   {
2329     CloseDoor(DOOR_CLOSE_1);
2330
2331     if (!(req_state & REQ_STAY_CLOSED) && (old_door_state & DOOR_OPEN_1))
2332     {
2333       BlitBitmap(bitmap_db_door, bitmap_db_door,
2334                  DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE,
2335                  DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2336       OpenDoor(DOOR_OPEN_1);
2337     }
2338   }
2339
2340   RemapAllGadgets();
2341
2342   SetDrawBackgroundMask(REDRAW_FIELD);
2343
2344 #if defined(NETWORK_AVALIABLE)
2345   /* continue network game after request */
2346   if (options.network &&
2347       game_status == GAME_MODE_PLAYING &&
2348       req_state & REQUEST_WAIT_FOR)
2349     SendToServer_ContinuePlaying();
2350 #endif
2351
2352 #if 1
2353   /* restore deactivated drawing when quick-loading level tape recording */
2354   if (tape.playing && tape.deactivate_display)
2355     TapeDeactivateDisplayOn();
2356 #endif
2357
2358   return result;
2359 }
2360
2361 unsigned int OpenDoor(unsigned int door_state)
2362 {
2363   unsigned int new_door_state;
2364
2365   if (door_state & DOOR_COPY_BACK)
2366   {
2367     BlitBitmap(bitmap_db_door, bitmap_db_door,
2368                DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE + VYSIZE,
2369                DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2370     door_state &= ~DOOR_COPY_BACK;
2371   }
2372
2373   new_door_state = MoveDoor(door_state);
2374
2375   return(new_door_state);
2376 }
2377
2378 unsigned int CloseDoor(unsigned int door_state)
2379 {
2380   unsigned int new_door_state;
2381
2382   BlitBitmap(backbuffer, bitmap_db_door,
2383              DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2384   BlitBitmap(backbuffer, bitmap_db_door,
2385              VX, VY, VXSIZE, VYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2);
2386
2387   new_door_state = MoveDoor(door_state);
2388
2389   return(new_door_state);
2390 }
2391
2392 unsigned int GetDoorState()
2393 {
2394   return MoveDoor(DOOR_GET_STATE);
2395 }
2396
2397 unsigned int SetDoorState(unsigned int door_state)
2398 {
2399   return MoveDoor(door_state | DOOR_SET_STATE);
2400 }
2401
2402 unsigned int MoveDoor(unsigned int door_state)
2403 {
2404   static int door1 = DOOR_OPEN_1;
2405   static int door2 = DOOR_CLOSE_2;
2406   unsigned long door_delay = 0;
2407   unsigned long door_delay_value;
2408   int stepsize = 1;
2409
2410   if (door_state == DOOR_GET_STATE)
2411     return(door1 | door2);
2412
2413   if (door_state & DOOR_SET_STATE)
2414   {
2415     if (door_state & DOOR_ACTION_1)
2416       door1 = door_state & DOOR_ACTION_1;
2417     if (door_state & DOOR_ACTION_2)
2418       door2 = door_state & DOOR_ACTION_2;
2419
2420     return(door1 | door2);
2421   }
2422
2423   if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
2424     door_state &= ~DOOR_OPEN_1;
2425   else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
2426     door_state &= ~DOOR_CLOSE_1;
2427   if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
2428     door_state &= ~DOOR_OPEN_2;
2429   else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
2430     door_state &= ~DOOR_CLOSE_2;
2431
2432   door_delay_value = (door_state & DOOR_ACTION_1 ? door_1.step_delay :
2433                       door_2.step_delay);
2434
2435   if (setup.quick_doors)
2436   {
2437     stepsize = 20;              /* must be choosen to always draw last frame */
2438     door_delay_value = 0;
2439
2440 #if 0
2441     StopSound(SND_DOOR_OPENING);
2442     StopSound(SND_DOOR_CLOSING);
2443 #endif
2444   }
2445
2446   if (global.autoplay_leveldir)
2447   {
2448     door_state |= DOOR_NO_DELAY;
2449     door_state &= ~DOOR_CLOSE_ALL;
2450   }
2451
2452   if (door_state & DOOR_ACTION)
2453   {
2454     boolean door_1_done = !(door_state & DOOR_ACTION_1);
2455     boolean door_2_done = !(door_state & DOOR_ACTION_2);
2456     int start = ((door_state & DOOR_NO_DELAY) ? DXSIZE : 0);
2457     int end = (door_state & DOOR_ACTION_1 &&
2458                door_1.anim_mode == ANIM_VERTICAL ? DYSIZE : DXSIZE);
2459     int x;
2460
2461     if (!(door_state & DOOR_NO_DELAY) && !setup.quick_doors)
2462     {
2463       /* opening door sound has priority over simultaneously closing door */
2464       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
2465         PlaySoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2466       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
2467         PlaySoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2468     }
2469
2470     for (x = start; x <= end && !(door_1_done && door_2_done); x += stepsize)
2471     {
2472       Bitmap *bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2473       GC gc = bitmap->stored_clip_gc;
2474
2475       if (door_state & DOOR_ACTION_1)
2476       {
2477         int a = MIN(x * door_1.step_offset, end);
2478         int i = (door_state & DOOR_OPEN_1 ? end - a : a);
2479
2480         if (x <= a)
2481         {
2482           BlitBitmap(bitmap_db_door, drawto,
2483                      DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1 + i / 2,
2484                      DXSIZE,DYSIZE - i / 2, DX, DY);
2485
2486           ClearRectangle(drawto, DX, DY + DYSIZE - i / 2, DXSIZE, i / 2);
2487         }
2488
2489         if (door_1.anim_mode == ANIM_HORIZONTAL && x <= DXSIZE)
2490         {
2491           int src1_x = DXSIZE,          src1_y = DOOR_GFX_PAGEY1;
2492           int dst1_x = DX + DXSIZE - i, dst1_y = DY;
2493           int src2_x = DXSIZE - i,      src2_y = DOOR_GFX_PAGEY1;
2494           int dst2_x = DX,              dst2_y = DY;
2495           int width = i, height = DYSIZE;
2496
2497           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
2498           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
2499                            dst1_x, dst1_y);
2500
2501           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
2502           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
2503                            dst2_x, dst2_y);
2504         }
2505         else if (door_1.anim_mode == ANIM_VERTICAL && x <= DYSIZE)
2506         {
2507           int src1_x = DXSIZE,          src1_y = DOOR_GFX_PAGEY1;
2508           int dst1_x = DX,              dst1_y = DY + DYSIZE - i;
2509           int src2_x = 0,               src2_y = DOOR_GFX_PAGEY1 + DYSIZE - i;
2510           int dst2_x = DX,              dst2_y = DY;
2511           int width = DXSIZE, height = i;
2512
2513           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
2514           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
2515                            dst1_x, dst1_y);
2516
2517           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
2518           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
2519                            dst2_x, dst2_y);
2520         }
2521         else if (x <= DXSIZE)   /* ANIM_DEFAULT */
2522         {
2523           int j = (door_1.anim_mode == ANIM_DEFAULT ? (DXSIZE - i) / 3 : 0);
2524
2525           SetClipOrigin(bitmap, gc, DX - i, (DY + j) - DOOR_GFX_PAGEY1);
2526           BlitBitmapMasked(bitmap, drawto,
2527                            DXSIZE, DOOR_GFX_PAGEY1, i, 77,
2528                            DX + DXSIZE - i, DY + j);
2529           BlitBitmapMasked(bitmap, drawto,
2530                            DXSIZE, DOOR_GFX_PAGEY1 + 140, i, 63,
2531                            DX + DXSIZE - i, DY + 140 + j);
2532           SetClipOrigin(bitmap, gc, DX - DXSIZE + i,
2533                         DY - (DOOR_GFX_PAGEY1 + j));
2534           BlitBitmapMasked(bitmap, drawto,
2535                            DXSIZE - i, DOOR_GFX_PAGEY1 + j, i, 77 - j,
2536                            DX, DY);
2537           BlitBitmapMasked(bitmap, drawto,
2538                            DXSIZE-i, DOOR_GFX_PAGEY1 + 140, i, 63,
2539                            DX, DY + 140 - j);
2540
2541           BlitBitmapMasked(bitmap, drawto,
2542                            DXSIZE - i, DOOR_GFX_PAGEY1 + 77, i, 63,
2543                            DX, DY + 77 - j);
2544           BlitBitmapMasked(bitmap, drawto,
2545                            DXSIZE - i, DOOR_GFX_PAGEY1 + 203, i, 77,
2546                            DX, DY + 203 - j);
2547           SetClipOrigin(bitmap, gc, DX - i, (DY + j) - DOOR_GFX_PAGEY1);
2548           BlitBitmapMasked(bitmap, drawto,
2549                            DXSIZE, DOOR_GFX_PAGEY1 + 77, i, 63,
2550                            DX + DXSIZE - i, DY + 77 + j);
2551           BlitBitmapMasked(bitmap, drawto,
2552                            DXSIZE, DOOR_GFX_PAGEY1 + 203, i, 77 - j,
2553                            DX + DXSIZE - i, DY + 203 + j);
2554         }
2555
2556         redraw_mask |= REDRAW_DOOR_1;
2557         door_1_done = (a == end);
2558       }
2559
2560       if (door_state & DOOR_ACTION_2)
2561       {
2562         int a = MIN(x * door_2.step_offset, VXSIZE);
2563         int i = (door_state & DOOR_OPEN_2 ? VXSIZE - a : a);
2564
2565         if (x <= VYSIZE)
2566         {
2567           BlitBitmap(bitmap_db_door, drawto,
2568                      DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2 + i / 2,
2569                      VXSIZE, VYSIZE - i / 2, VX, VY);
2570
2571           ClearRectangle(drawto, VX, VY + VYSIZE - i / 2, VXSIZE, i / 2);
2572         }
2573
2574         if (door_2.anim_mode == ANIM_HORIZONTAL && x <= VXSIZE)
2575         {
2576           int src1_x = VXSIZE,          src1_y = DOOR_GFX_PAGEY2;
2577           int dst1_x = VX + VXSIZE - i, dst1_y = VY;
2578           int src2_x = VXSIZE - i,      src2_y = DOOR_GFX_PAGEY2;
2579           int dst2_x = VX,              dst2_y = VY;
2580           int width = i, height = VYSIZE;
2581
2582           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
2583           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
2584                            dst1_x, dst1_y);
2585
2586           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
2587           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
2588                            dst2_x, dst2_y);
2589         }
2590         else if (door_2.anim_mode == ANIM_VERTICAL && x <= VYSIZE)
2591         {
2592           int src1_x = VXSIZE,          src1_y = DOOR_GFX_PAGEY2;
2593           int dst1_x = VX,              dst1_y = VY + VYSIZE - i;
2594           int src2_x = 0,               src2_y = DOOR_GFX_PAGEY2 + VYSIZE - i;
2595           int dst2_x = VX,              dst2_y = VY;
2596           int width = VXSIZE, height = i;
2597
2598           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
2599           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
2600                            dst1_x, dst1_y);
2601
2602           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
2603           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
2604                            dst2_x, dst2_y);
2605         }
2606         else if (x <= VXSIZE)   /* ANIM_DEFAULT */
2607         {
2608           int j = (door_2.anim_mode == ANIM_DEFAULT ? (VXSIZE - i) / 3 : 0);
2609
2610           SetClipOrigin(bitmap, gc, VX - i, (VY + j) - DOOR_GFX_PAGEY2);
2611           BlitBitmapMasked(bitmap, drawto,
2612                            VXSIZE, DOOR_GFX_PAGEY2, i, VYSIZE / 2,
2613                            VX + VXSIZE - i, VY + j);
2614           SetClipOrigin(bitmap, gc,
2615                         VX - VXSIZE + i, VY - (DOOR_GFX_PAGEY2 + j));
2616           BlitBitmapMasked(bitmap, drawto,
2617                            VXSIZE - i, DOOR_GFX_PAGEY2 + j, i, VYSIZE / 2 - j,
2618                            VX, VY);
2619
2620           BlitBitmapMasked(bitmap, drawto,
2621                            VXSIZE - i, DOOR_GFX_PAGEY2 + VYSIZE / 2,
2622                            i, VYSIZE / 2, VX, VY + VYSIZE / 2 - j);
2623           SetClipOrigin(bitmap, gc, VX - i, (VY + j) - DOOR_GFX_PAGEY2);
2624           BlitBitmapMasked(bitmap, drawto,
2625                            VXSIZE, DOOR_GFX_PAGEY2 + VYSIZE / 2,
2626                            i, VYSIZE / 2 - j,
2627                            VX + VXSIZE - i, VY + VYSIZE / 2 + j);
2628         }
2629
2630         redraw_mask |= REDRAW_DOOR_2;
2631         door_2_done = (a == VXSIZE);
2632       }
2633
2634       BackToFront();
2635
2636       if (game_status == GAME_MODE_MAIN)
2637         DoAnimation();
2638
2639       if (!(door_state & DOOR_NO_DELAY))
2640         WaitUntilDelayReached(&door_delay, door_delay_value);
2641     }
2642   }
2643
2644 #if 0
2645   if (setup.quick_doors)
2646   {
2647     StopSound(SND_DOOR_OPENING);
2648     StopSound(SND_DOOR_CLOSING);
2649   }
2650 #endif
2651
2652   if (door_state & DOOR_ACTION_1)
2653     door1 = door_state & DOOR_ACTION_1;
2654   if (door_state & DOOR_ACTION_2)
2655     door2 = door_state & DOOR_ACTION_2;
2656
2657   return (door1 | door2);
2658 }
2659
2660 void DrawSpecialEditorDoor()
2661 {
2662   /* draw bigger toolbox window */
2663   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2664              DOOR_GFX_PAGEX7, 0, EXSIZE + 8, 8,
2665              EX - 4, EY - 12);
2666   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
2667              EX - 4, VY - 4, EXSIZE + 8, EYSIZE - VYSIZE + 4,
2668              EX - 4, EY - 4);
2669
2670   redraw_mask |= REDRAW_ALL;
2671 }
2672
2673 void UndrawSpecialEditorDoor()
2674 {
2675   /* draw normal tape recorder window */
2676   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
2677              EX - 4, EY - 12, EXSIZE + 8, EYSIZE - VYSIZE + 12,
2678              EX - 4, EY - 12);
2679
2680   redraw_mask |= REDRAW_ALL;
2681 }
2682
2683
2684 /* ---------- new tool button stuff ---------------------------------------- */
2685
2686 /* graphic position values for tool buttons */
2687 #define TOOL_BUTTON_YES_XPOS            2
2688 #define TOOL_BUTTON_YES_YPOS            250
2689 #define TOOL_BUTTON_YES_GFX_YPOS        0
2690 #define TOOL_BUTTON_YES_XSIZE           46
2691 #define TOOL_BUTTON_YES_YSIZE           28
2692 #define TOOL_BUTTON_NO_XPOS             52
2693 #define TOOL_BUTTON_NO_YPOS             TOOL_BUTTON_YES_YPOS
2694 #define TOOL_BUTTON_NO_GFX_YPOS         TOOL_BUTTON_YES_GFX_YPOS
2695 #define TOOL_BUTTON_NO_XSIZE            TOOL_BUTTON_YES_XSIZE
2696 #define TOOL_BUTTON_NO_YSIZE            TOOL_BUTTON_YES_YSIZE
2697 #define TOOL_BUTTON_CONFIRM_XPOS        TOOL_BUTTON_YES_XPOS
2698 #define TOOL_BUTTON_CONFIRM_YPOS        TOOL_BUTTON_YES_YPOS
2699 #define TOOL_BUTTON_CONFIRM_GFX_YPOS    30
2700 #define TOOL_BUTTON_CONFIRM_XSIZE       96
2701 #define TOOL_BUTTON_CONFIRM_YSIZE       TOOL_BUTTON_YES_YSIZE
2702 #define TOOL_BUTTON_PLAYER_XSIZE        30
2703 #define TOOL_BUTTON_PLAYER_YSIZE        30
2704 #define TOOL_BUTTON_PLAYER_GFX_XPOS     5
2705 #define TOOL_BUTTON_PLAYER_GFX_YPOS     185
2706 #define TOOL_BUTTON_PLAYER_XPOS         (5 + TOOL_BUTTON_PLAYER_XSIZE / 2)
2707 #define TOOL_BUTTON_PLAYER_YPOS         (215 - TOOL_BUTTON_PLAYER_YSIZE / 2)
2708 #define TOOL_BUTTON_PLAYER1_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2709                                          + 0 * TOOL_BUTTON_PLAYER_XSIZE)
2710 #define TOOL_BUTTON_PLAYER2_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2711                                          + 1 * TOOL_BUTTON_PLAYER_XSIZE)
2712 #define TOOL_BUTTON_PLAYER3_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2713                                          + 0 * TOOL_BUTTON_PLAYER_XSIZE)
2714 #define TOOL_BUTTON_PLAYER4_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2715                                          + 1 * TOOL_BUTTON_PLAYER_XSIZE)
2716 #define TOOL_BUTTON_PLAYER1_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2717                                          + 0 * TOOL_BUTTON_PLAYER_YSIZE)
2718 #define TOOL_BUTTON_PLAYER2_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2719                                          + 0 * TOOL_BUTTON_PLAYER_YSIZE)
2720 #define TOOL_BUTTON_PLAYER3_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2721                                          + 1 * TOOL_BUTTON_PLAYER_YSIZE)
2722 #define TOOL_BUTTON_PLAYER4_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2723                                          + 1 * TOOL_BUTTON_PLAYER_YSIZE)
2724
2725 static struct
2726 {
2727   int xpos, ypos;
2728   int x, y;
2729   int width, height;
2730   int gadget_id;
2731   char *infotext;
2732 } toolbutton_info[NUM_TOOL_BUTTONS] =
2733 {
2734   {
2735     TOOL_BUTTON_YES_XPOS,       TOOL_BUTTON_YES_GFX_YPOS,
2736     TOOL_BUTTON_YES_XPOS,       TOOL_BUTTON_YES_YPOS,
2737     TOOL_BUTTON_YES_XSIZE,      TOOL_BUTTON_YES_YSIZE,
2738     TOOL_CTRL_ID_YES,
2739     "yes"
2740   },
2741   {
2742     TOOL_BUTTON_NO_XPOS,        TOOL_BUTTON_NO_GFX_YPOS,
2743     TOOL_BUTTON_NO_XPOS,        TOOL_BUTTON_NO_YPOS,
2744     TOOL_BUTTON_NO_XSIZE,       TOOL_BUTTON_NO_YSIZE,
2745     TOOL_CTRL_ID_NO,
2746     "no"
2747   },
2748   {
2749     TOOL_BUTTON_CONFIRM_XPOS,   TOOL_BUTTON_CONFIRM_GFX_YPOS,
2750     TOOL_BUTTON_CONFIRM_XPOS,   TOOL_BUTTON_CONFIRM_YPOS,
2751     TOOL_BUTTON_CONFIRM_XSIZE,  TOOL_BUTTON_CONFIRM_YSIZE,
2752     TOOL_CTRL_ID_CONFIRM,
2753     "confirm"
2754   },
2755   {
2756     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2757     TOOL_BUTTON_PLAYER1_XPOS,   TOOL_BUTTON_PLAYER1_YPOS,
2758     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2759     TOOL_CTRL_ID_PLAYER_1,
2760     "player 1"
2761   },
2762   {
2763     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2764     TOOL_BUTTON_PLAYER2_XPOS,   TOOL_BUTTON_PLAYER2_YPOS,
2765     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2766     TOOL_CTRL_ID_PLAYER_2,
2767     "player 2"
2768   },
2769   {
2770     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2771     TOOL_BUTTON_PLAYER3_XPOS,   TOOL_BUTTON_PLAYER3_YPOS,
2772     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2773     TOOL_CTRL_ID_PLAYER_3,
2774     "player 3"
2775   },
2776   {
2777     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2778     TOOL_BUTTON_PLAYER4_XPOS,   TOOL_BUTTON_PLAYER4_YPOS,
2779     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2780     TOOL_CTRL_ID_PLAYER_4,
2781     "player 4"
2782   }
2783 };
2784
2785 void CreateToolButtons()
2786 {
2787   int i;
2788
2789   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2790   {
2791     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2792     Bitmap *deco_bitmap = None;
2793     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
2794     struct GadgetInfo *gi;
2795     unsigned long event_mask;
2796     int gd_xoffset, gd_yoffset;
2797     int gd_x1, gd_x2, gd_y;
2798     int id = i;
2799
2800     event_mask = GD_EVENT_RELEASED;
2801
2802     gd_xoffset = toolbutton_info[i].xpos;
2803     gd_yoffset = toolbutton_info[i].ypos;
2804     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
2805     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
2806     gd_y = DOOR_GFX_PAGEY1 + gd_yoffset;
2807
2808     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
2809     {
2810       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
2811
2812       getMiniGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr),
2813                            &deco_bitmap, &deco_x, &deco_y);
2814       deco_xpos = (toolbutton_info[i].width - MINI_TILEX) / 2;
2815       deco_ypos = (toolbutton_info[i].height - MINI_TILEY) / 2;
2816     }
2817
2818     gi = CreateGadget(GDI_CUSTOM_ID, id,
2819                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
2820                       GDI_X, DX + toolbutton_info[i].x,
2821                       GDI_Y, DY + toolbutton_info[i].y,
2822                       GDI_WIDTH, toolbutton_info[i].width,
2823                       GDI_HEIGHT, toolbutton_info[i].height,
2824                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
2825                       GDI_STATE, GD_BUTTON_UNPRESSED,
2826                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y,
2827                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y,
2828                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
2829                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
2830                       GDI_DECORATION_SIZE, MINI_TILEX, MINI_TILEY,
2831                       GDI_DECORATION_SHIFTING, 1, 1,
2832                       GDI_EVENT_MASK, event_mask,
2833                       GDI_CALLBACK_ACTION, HandleToolButtons,
2834                       GDI_END);
2835
2836     if (gi == NULL)
2837       Error(ERR_EXIT, "cannot create gadget");
2838
2839     tool_gadget[id] = gi;
2840   }
2841 }
2842
2843 void FreeToolButtons()
2844 {
2845   int i;
2846
2847   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2848     FreeGadget(tool_gadget[i]);
2849 }
2850
2851 static void UnmapToolButtons()
2852 {
2853   int i;
2854
2855   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2856     UnmapGadget(tool_gadget[i]);
2857 }
2858
2859 static void HandleToolButtons(struct GadgetInfo *gi)
2860 {
2861   request_gadget_id = gi->custom_id;
2862 }
2863
2864 int get_next_element(int element)
2865 {
2866   switch(element)
2867   {
2868     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
2869     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
2870     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
2871     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
2872     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
2873     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
2874     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
2875
2876     default:                            return element;
2877   }
2878 }
2879
2880 int el_act_dir2img(int element, int action, int direction)
2881 {
2882   element = GFX_ELEMENT(element);
2883   direction = MV_DIR_BIT(direction);
2884
2885   return element_info[element].direction_graphic[action][direction];
2886 }
2887
2888 static int el_act_dir2crm(int element, int action, int direction)
2889 {
2890   element = GFX_ELEMENT(element);
2891   direction = MV_DIR_BIT(direction);
2892
2893   return element_info[element].direction_crumbled[action][direction];
2894 }
2895
2896 int el_act2img(int element, int action)
2897 {
2898   element = GFX_ELEMENT(element);
2899
2900   return element_info[element].graphic[action];
2901 }
2902
2903 int el_act2crm(int element, int action)
2904 {
2905   element = GFX_ELEMENT(element);
2906
2907   return element_info[element].crumbled[action];
2908 }
2909
2910 int el_dir2img(int element, int direction)
2911 {
2912   element = GFX_ELEMENT(element);
2913
2914   return el_act_dir2img(element, ACTION_DEFAULT, direction);
2915 }
2916
2917 int el2baseimg(int element)
2918 {
2919   return element_info[element].graphic[ACTION_DEFAULT];
2920 }
2921
2922 int el2img(int element)
2923 {
2924   element = GFX_ELEMENT(element);
2925
2926   return element_info[element].graphic[ACTION_DEFAULT];
2927 }
2928
2929 int el2edimg(int element)
2930 {
2931   element = GFX_ELEMENT(element);
2932
2933   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
2934 }
2935
2936 int el2preimg(int element)
2937 {
2938   element = GFX_ELEMENT(element);
2939
2940   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
2941 }