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