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