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