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