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