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