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