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