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