rnd-20040621-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 #if 1
1706   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
1707   int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
1708 #else
1709   boolean draw_masked = graphic_info[graphic].draw_masked;
1710   int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_ON_BACKGROUND);
1711 #endif
1712   boolean ffwd_delay = (tape.playing && tape.fast_forward);
1713   boolean no_delay = (tape.warp_forward);
1714   unsigned long anim_delay = 0;
1715   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
1716   int anim_delay_value = (no_delay ? 0 : frame_delay_value);
1717   int font_nr = FONT_ENVELOPE_1 + envelope_nr;
1718   int font_width = getFontWidth(font_nr);
1719   int font_height = getFontHeight(font_nr);
1720   int max_xsize = level.envelope_xsize[envelope_nr];
1721   int max_ysize = level.envelope_ysize[envelope_nr];
1722   int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
1723   int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
1724   int xend = max_xsize;
1725   int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
1726   int xstep = (xstart < xend ? 1 : 0);
1727   int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
1728   int x, y;
1729
1730   for (x = xstart, y = ystart; x <= xend && y <= yend; x += xstep, y += ystep)
1731   {
1732     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
1733     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
1734     int sx = (SXSIZE - xsize * font_width)  / 2;
1735     int sy = (SYSIZE - ysize * font_height) / 2;
1736     int xx, yy;
1737
1738     SetDrawtoField(DRAW_BUFFERED);
1739
1740     BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1741
1742     SetDrawtoField(DRAW_BACKBUFFER);
1743
1744     for (yy = 0; yy < ysize; yy++) for (xx = 0; xx < xsize; xx++)
1745       DrawEnvelopeBackground(envelope_nr, sx,sy, xx,yy, xsize, ysize, font_nr);
1746
1747     DrawTextToTextArea(SX + sx + font_width, SY + sy + font_height,
1748                        level.envelope_text[envelope_nr], font_nr, max_xsize,
1749                        xsize - 2, ysize - 2, mask_mode);
1750
1751     redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
1752     BackToFront();
1753
1754     WaitUntilDelayReached(&anim_delay, anim_delay_value / 2);
1755   }
1756 }
1757
1758 void ShowEnvelope(int envelope_nr)
1759 {
1760   int element = EL_ENVELOPE_1 + envelope_nr;
1761   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
1762   int sound_opening = element_info[element].sound[ACTION_OPENING];
1763   int sound_closing = element_info[element].sound[ACTION_CLOSING];
1764   boolean ffwd_delay = (tape.playing && tape.fast_forward);
1765   boolean no_delay = (tape.warp_forward);
1766   int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
1767   int wait_delay_value = (no_delay ? 0 : normal_delay_value);
1768   int anim_mode = graphic_info[graphic].anim_mode;
1769   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
1770                         anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
1771
1772   game.envelope_active = TRUE;  /* needed for RedrawPlayfield() events */
1773
1774   PlaySoundStereo(sound_opening, SOUND_MIDDLE);
1775
1776   if (anim_mode == ANIM_DEFAULT)
1777     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
1778
1779   AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
1780
1781   if (tape.playing)
1782     Delay(wait_delay_value);
1783   else
1784     WaitForEventToContinue();
1785
1786   PlaySoundStereo(sound_closing, SOUND_MIDDLE);
1787
1788   if (anim_mode != ANIM_NONE)
1789     AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
1790
1791   if (anim_mode == ANIM_DEFAULT)
1792     AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
1793
1794   game.envelope_active = FALSE;
1795
1796   SetDrawtoField(DRAW_BUFFERED);
1797
1798   redraw_mask |= REDRAW_FIELD;
1799   BackToFront();
1800 }
1801
1802 void getMicroGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1803 {
1804   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
1805   int mini_startx = src_bitmap->width * 3 / 4;
1806   int mini_starty = src_bitmap->height * 2 / 3;
1807   int src_x = mini_startx + graphic_info[graphic].src_x / 8;
1808   int src_y = mini_starty + graphic_info[graphic].src_y / 8;
1809
1810   *bitmap = src_bitmap;
1811   *x = src_x;
1812   *y = src_y;
1813 }
1814
1815 void DrawMicroElement(int xpos, int ypos, int element)
1816 {
1817   Bitmap *src_bitmap;
1818   int src_x, src_y;
1819   int graphic = el2preimg(element);
1820
1821   getMicroGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1822   BlitBitmap(src_bitmap, drawto, src_x, src_y, MICRO_TILEX, MICRO_TILEY,
1823              xpos, ypos);
1824 }
1825
1826 void DrawLevel()
1827 {
1828   int x,y;
1829
1830   SetDrawBackgroundMask(REDRAW_NONE);
1831   ClearWindow();
1832
1833   for (x = BX1; x <= BX2; x++)
1834     for (y = BY1; y <= BY2; y++)
1835       DrawScreenField(x, y);
1836
1837   redraw_mask |= REDRAW_FIELD;
1838 }
1839
1840 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
1841 {
1842   int x,y;
1843
1844   for (x = 0; x < size_x; x++)
1845     for (y = 0; y < size_y; y++)
1846       DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
1847
1848   redraw_mask |= REDRAW_FIELD;
1849 }
1850
1851 static void DrawMicroLevelExt(int xpos, int ypos, int from_x, int from_y)
1852 {
1853   int x, y;
1854
1855   DrawBackground(xpos, ypos, MICROLEV_XSIZE, MICROLEV_YSIZE);
1856
1857   if (lev_fieldx < STD_LEV_FIELDX)
1858     xpos += (STD_LEV_FIELDX - lev_fieldx) / 2 * MICRO_TILEX;
1859   if (lev_fieldy < STD_LEV_FIELDY)
1860     ypos += (STD_LEV_FIELDY - lev_fieldy) / 2 * MICRO_TILEY;
1861
1862   xpos += MICRO_TILEX;
1863   ypos += MICRO_TILEY;
1864
1865   for (x = -1; x <= STD_LEV_FIELDX; x++)
1866   {
1867     for (y = -1; y <= STD_LEV_FIELDY; y++)
1868     {
1869       int lx = from_x + x, ly = from_y + y;
1870
1871       if (lx >= 0 && lx < lev_fieldx && ly >= 0 && ly < lev_fieldy)
1872         DrawMicroElement(xpos + x * MICRO_TILEX, ypos + y * MICRO_TILEY,
1873                          level.field[lx][ly]);
1874       else if (lx >= -1 && lx < lev_fieldx+1 && ly >= -1 && ly < lev_fieldy+1
1875                && BorderElement != EL_EMPTY)
1876         DrawMicroElement(xpos + x * MICRO_TILEX, ypos + y * MICRO_TILEY,
1877                          getBorderElement(lx, ly));
1878     }
1879   }
1880
1881   redraw_mask |= REDRAW_MICROLEVEL;
1882 }
1883
1884 #define MICROLABEL_EMPTY                0
1885 #define MICROLABEL_LEVEL_NAME           1
1886 #define MICROLABEL_CREATED_BY           2
1887 #define MICROLABEL_LEVEL_AUTHOR         3
1888 #define MICROLABEL_IMPORTED_FROM        4
1889 #define MICROLABEL_LEVEL_IMPORT_INFO    5
1890
1891 static void DrawMicroLevelLabelExt(int mode)
1892 {
1893   char label_text[MAX_OUTPUT_LINESIZE + 1];
1894   int max_len_label_text;
1895   int font_nr = FONT_TEXT_2;
1896
1897   if (mode == MICROLABEL_CREATED_BY || mode == MICROLABEL_IMPORTED_FROM)
1898     font_nr = FONT_TEXT_3;
1899
1900   max_len_label_text = SXSIZE / getFontWidth(font_nr);
1901
1902   DrawBackground(SX, MICROLABEL_YPOS, SXSIZE, getFontHeight(font_nr));
1903
1904   strncpy(label_text, (mode == MICROLABEL_LEVEL_NAME ? level.name :
1905                        mode == MICROLABEL_CREATED_BY ? "created by" :
1906                        mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
1907                        mode == MICROLABEL_IMPORTED_FROM ? "imported from" :
1908                        mode == MICROLABEL_LEVEL_IMPORT_INFO ?
1909                        leveldir_current->imported_from : ""),
1910           max_len_label_text);
1911   label_text[max_len_label_text] = '\0';
1912
1913   if (strlen(label_text) > 0)
1914   {
1915     int lxpos = SX + (SXSIZE - getTextWidth(label_text, font_nr)) / 2;
1916     int lypos = MICROLABEL_YPOS;
1917
1918     DrawText(lxpos, lypos, label_text, font_nr);
1919   }
1920
1921   redraw_mask |= REDRAW_MICROLEVEL;
1922 }
1923
1924 void DrawMicroLevel(int xpos, int ypos, boolean restart)
1925 {
1926   static unsigned long scroll_delay = 0;
1927   static unsigned long label_delay = 0;
1928   static int from_x, from_y, scroll_direction;
1929   static int label_state, label_counter;
1930   int last_game_status = game_status;   /* save current game status */
1931
1932   /* force PREVIEW font on preview level */
1933   game_status = GAME_MODE_PSEUDO_PREVIEW;
1934
1935   if (restart)
1936   {
1937     from_x = from_y = 0;
1938     scroll_direction = MV_RIGHT;
1939     label_state = 1;
1940     label_counter = 0;
1941
1942     DrawMicroLevelExt(xpos, ypos, from_x, from_y);
1943     DrawMicroLevelLabelExt(label_state);
1944
1945     /* initialize delay counters */
1946     DelayReached(&scroll_delay, 0);
1947     DelayReached(&label_delay, 0);
1948
1949     if (leveldir_current->name)
1950     {
1951       int text_width = getTextWidth(leveldir_current->name, FONT_TEXT_1);
1952       int lxpos = SX + (SXSIZE - text_width) / 2;
1953       int lypos = SY + 352;
1954
1955       DrawText(lxpos, lypos, leveldir_current->name, FONT_TEXT_1);
1956     }
1957
1958     game_status = last_game_status;     /* restore current game status */
1959
1960     return;
1961   }
1962
1963   /* scroll micro level, if needed */
1964   if ((lev_fieldx > STD_LEV_FIELDX || lev_fieldy > STD_LEV_FIELDY) &&
1965       DelayReached(&scroll_delay, MICROLEVEL_SCROLL_DELAY))
1966   {
1967     switch (scroll_direction)
1968     {
1969       case MV_LEFT:
1970         if (from_x > 0)
1971           from_x--;
1972         else
1973           scroll_direction = MV_UP;
1974         break;
1975
1976       case MV_RIGHT:
1977         if (from_x < lev_fieldx - STD_LEV_FIELDX)
1978           from_x++;
1979         else
1980           scroll_direction = MV_DOWN;
1981         break;
1982
1983       case MV_UP:
1984         if (from_y > 0)
1985           from_y--;
1986         else
1987           scroll_direction = MV_RIGHT;
1988         break;
1989
1990       case MV_DOWN:
1991         if (from_y < lev_fieldy - STD_LEV_FIELDY)
1992           from_y++;
1993         else
1994           scroll_direction = MV_LEFT;
1995         break;
1996
1997       default:
1998         break;
1999     }
2000
2001     DrawMicroLevelExt(xpos, ypos, from_x, from_y);
2002   }
2003
2004   /* redraw micro level label, if needed */
2005   if (strcmp(level.name, NAMELESS_LEVEL_NAME) != 0 &&
2006       strcmp(level.author, ANONYMOUS_NAME) != 0 &&
2007       strcmp(level.author, leveldir_current->name) != 0 &&
2008       DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2009   {
2010     int max_label_counter = 23;
2011
2012     if (leveldir_current->imported_from != NULL)
2013       max_label_counter += 14;
2014
2015     label_counter = (label_counter + 1) % max_label_counter;
2016     label_state = (label_counter >= 0 && label_counter <= 7 ?
2017                    MICROLABEL_LEVEL_NAME :
2018                    label_counter >= 9 && label_counter <= 12 ?
2019                    MICROLABEL_CREATED_BY :
2020                    label_counter >= 14 && label_counter <= 21 ?
2021                    MICROLABEL_LEVEL_AUTHOR :
2022                    label_counter >= 23 && label_counter <= 26 ?
2023                    MICROLABEL_IMPORTED_FROM :
2024                    label_counter >= 28 && label_counter <= 35 ?
2025                    MICROLABEL_LEVEL_IMPORT_INFO : MICROLABEL_EMPTY);
2026     DrawMicroLevelLabelExt(label_state);
2027   }
2028
2029   game_status = last_game_status;       /* restore current game status */
2030 }
2031
2032 void WaitForEventToContinue()
2033 {
2034   boolean still_wait = TRUE;
2035
2036   /* simulate releasing mouse button over last gadget, if still pressed */
2037   if (button_status)
2038     HandleGadgets(-1, -1, 0);
2039
2040   button_status = MB_RELEASED;
2041
2042   while (still_wait)
2043   {
2044     if (PendingEvent())
2045     {
2046       Event event;
2047
2048       NextEvent(&event);
2049
2050       switch (event.type)
2051       {
2052         case EVENT_BUTTONPRESS:
2053         case EVENT_KEYPRESS:
2054           still_wait = FALSE;
2055           break;
2056
2057         case EVENT_KEYRELEASE:
2058           ClearPlayerAction();
2059           break;
2060
2061         default:
2062           HandleOtherEvents(&event);
2063           break;
2064       }
2065     }
2066     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
2067     {
2068       still_wait = FALSE;
2069     }
2070
2071     DoAnimation();
2072
2073     /* don't eat all CPU time */
2074     Delay(10);
2075   }
2076 }
2077
2078 #define MAX_REQUEST_LINES               13
2079 #define MAX_REQUEST_LINE_FONT1_LEN      7
2080 #define MAX_REQUEST_LINE_FONT2_LEN      10
2081
2082 boolean Request(char *text, unsigned int req_state)
2083 {
2084   int mx, my, ty, result = -1;
2085   unsigned int old_door_state;
2086   int last_game_status = game_status;   /* save current game status */
2087   int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
2088   int font_nr = FONT_TEXT_2;
2089   int max_word_len = 0;
2090   char *text_ptr;
2091
2092   for (text_ptr = text; *text_ptr; text_ptr++)
2093   {
2094     max_word_len = (*text_ptr != ' ' ? max_word_len + 1 : 0);
2095
2096     if (max_word_len > MAX_REQUEST_LINE_FONT1_LEN)
2097     {
2098       max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
2099       font_nr = FONT_LEVEL_NUMBER;
2100
2101       break;
2102     }
2103   }
2104
2105 #if 1
2106   /* disable deactivated drawing when quick-loading level tape recording */
2107   if (tape.playing && tape.deactivate_display)
2108     TapeDeactivateDisplayOff(TRUE);
2109 #endif
2110
2111 #if 1
2112   SetMouseCursor(CURSOR_DEFAULT);
2113 #endif
2114
2115 #if defined(NETWORK_AVALIABLE)
2116   /* pause network game while waiting for request to answer */
2117   if (options.network &&
2118       game_status == GAME_MODE_PLAYING &&
2119       req_state & REQUEST_WAIT_FOR_INPUT)
2120     SendToServer_PausePlaying();
2121 #endif
2122
2123   old_door_state = GetDoorState();
2124
2125   /* simulate releasing mouse button over last gadget, if still pressed */
2126   if (button_status)
2127     HandleGadgets(-1, -1, 0);
2128
2129   UnmapAllGadgets();
2130
2131   if (old_door_state & DOOR_OPEN_1)
2132   {
2133     CloseDoor(DOOR_CLOSE_1);
2134
2135     /* save old door content */
2136     BlitBitmap(bitmap_db_door, bitmap_db_door,
2137                DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE,
2138                DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1);
2139   }
2140
2141   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
2142
2143   /* clear door drawing field */
2144   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2145
2146   /* force DOOR font on preview level */
2147   game_status = GAME_MODE_PSEUDO_DOOR;
2148
2149   /* write text for request */
2150   for (ty = 0; ty < MAX_REQUEST_LINES; ty++)
2151   {
2152     char text_line[max_request_line_len + 1];
2153     int tx, tl, tc = 0;
2154
2155     if (!*text)
2156       break;
2157
2158     for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
2159     {
2160       tc = *(text + tx);
2161       if (!tc || tc == ' ')
2162         break;
2163     }
2164
2165     if (!tl)
2166     { 
2167       text++; 
2168       ty--; 
2169       continue; 
2170     }
2171
2172     strncpy(text_line, text, tl);
2173     text_line[tl] = 0;
2174
2175     DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
2176              DY + 8 + ty * (getFontHeight(font_nr) + 2),
2177              text_line, font_nr);
2178
2179     text += tl + (tc == ' ' ? 1 : 0);
2180   }
2181
2182   game_status = last_game_status;       /* restore current game status */
2183
2184   if (req_state & REQ_ASK)
2185   {
2186     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2187     MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2188   }
2189   else if (req_state & REQ_CONFIRM)
2190   {
2191     MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2192   }
2193   else if (req_state & REQ_PLAYER)
2194   {
2195     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2196     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2197     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2198     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2199   }
2200
2201   /* copy request gadgets to door backbuffer */
2202   BlitBitmap(drawto, bitmap_db_door,
2203              DX, DY, DXSIZE, DYSIZE,
2204              DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2205
2206   OpenDoor(DOOR_OPEN_1);
2207
2208 #if 0
2209   ClearEventQueue();
2210 #endif
2211
2212   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
2213   {
2214     SetDrawBackgroundMask(REDRAW_FIELD);
2215
2216     return FALSE;
2217   }
2218
2219   if (game_status != GAME_MODE_MAIN)
2220     InitAnimation();
2221
2222   button_status = MB_RELEASED;
2223
2224   request_gadget_id = -1;
2225
2226   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
2227
2228 #if 0
2229   SetMouseCursor(CURSOR_DEFAULT);
2230 #endif
2231
2232   while (result < 0)
2233   {
2234     if (PendingEvent())
2235     {
2236       Event event;
2237
2238       NextEvent(&event);
2239
2240       switch(event.type)
2241       {
2242         case EVENT_BUTTONPRESS:
2243         case EVENT_BUTTONRELEASE:
2244         case EVENT_MOTIONNOTIFY:
2245         {
2246           if (event.type == EVENT_MOTIONNOTIFY)
2247           {
2248             if (!PointerInWindow(window))
2249               continue; /* window and pointer are on different screens */
2250
2251             if (!button_status)
2252               continue;
2253
2254             motion_status = TRUE;
2255             mx = ((MotionEvent *) &event)->x;
2256             my = ((MotionEvent *) &event)->y;
2257           }
2258           else
2259           {
2260             motion_status = FALSE;
2261             mx = ((ButtonEvent *) &event)->x;
2262             my = ((ButtonEvent *) &event)->y;
2263             if (event.type == EVENT_BUTTONPRESS)
2264               button_status = ((ButtonEvent *) &event)->button;
2265             else
2266               button_status = MB_RELEASED;
2267           }
2268
2269           /* this sets 'request_gadget_id' */
2270           HandleGadgets(mx, my, button_status);
2271
2272           switch(request_gadget_id)
2273           {
2274             case TOOL_CTRL_ID_YES:
2275               result = TRUE;
2276               break;
2277             case TOOL_CTRL_ID_NO:
2278               result = FALSE;
2279               break;
2280             case TOOL_CTRL_ID_CONFIRM:
2281               result = TRUE | FALSE;
2282               break;
2283
2284             case TOOL_CTRL_ID_PLAYER_1:
2285               result = 1;
2286               break;
2287             case TOOL_CTRL_ID_PLAYER_2:
2288               result = 2;
2289               break;
2290             case TOOL_CTRL_ID_PLAYER_3:
2291               result = 3;
2292               break;
2293             case TOOL_CTRL_ID_PLAYER_4:
2294               result = 4;
2295               break;
2296
2297             default:
2298               break;
2299           }
2300
2301           break;
2302         }
2303
2304         case EVENT_KEYPRESS:
2305           switch(GetEventKey((KeyEvent *)&event, TRUE))
2306           {
2307             case KSYM_Return:
2308               result = 1;
2309               break;
2310
2311             case KSYM_Escape:
2312               result = 0;
2313               break;
2314
2315             default:
2316               break;
2317           }
2318           if (req_state & REQ_PLAYER)
2319             result = 0;
2320           break;
2321
2322         case EVENT_KEYRELEASE:
2323           ClearPlayerAction();
2324           break;
2325
2326         default:
2327           HandleOtherEvents(&event);
2328           break;
2329       }
2330     }
2331     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
2332     {
2333       int joy = AnyJoystick();
2334
2335       if (joy & JOY_BUTTON_1)
2336         result = 1;
2337       else if (joy & JOY_BUTTON_2)
2338         result = 0;
2339     }
2340
2341     DoAnimation();
2342
2343     /* don't eat all CPU time */
2344     Delay(10);
2345   }
2346
2347   if (game_status != GAME_MODE_MAIN)
2348     StopAnimation();
2349
2350   UnmapToolButtons();
2351
2352   if (!(req_state & REQ_STAY_OPEN))
2353   {
2354     CloseDoor(DOOR_CLOSE_1);
2355
2356     if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
2357         (req_state & REQ_REOPEN))
2358       OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
2359   }
2360
2361   RemapAllGadgets();
2362
2363   SetDrawBackgroundMask(REDRAW_FIELD);
2364
2365 #if defined(NETWORK_AVALIABLE)
2366   /* continue network game after request */
2367   if (options.network &&
2368       game_status == GAME_MODE_PLAYING &&
2369       req_state & REQUEST_WAIT_FOR_INPUT)
2370     SendToServer_ContinuePlaying();
2371 #endif
2372
2373 #if 1
2374   /* restore deactivated drawing when quick-loading level tape recording */
2375   if (tape.playing && tape.deactivate_display)
2376     TapeDeactivateDisplayOn();
2377 #endif
2378
2379   return result;
2380 }
2381
2382 unsigned int OpenDoor(unsigned int door_state)
2383 {
2384   if (door_state & DOOR_COPY_BACK)
2385   {
2386     if (door_state & DOOR_OPEN_1)
2387       BlitBitmap(bitmap_db_door, bitmap_db_door,
2388                  DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE,
2389                  DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2390
2391     if (door_state & DOOR_OPEN_2)
2392       BlitBitmap(bitmap_db_door, bitmap_db_door,
2393                  DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY2, VXSIZE, VYSIZE,
2394                  DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2);
2395
2396     door_state &= ~DOOR_COPY_BACK;
2397   }
2398
2399   return MoveDoor(door_state);
2400 }
2401
2402 unsigned int CloseDoor(unsigned int door_state)
2403 {
2404   unsigned int old_door_state = GetDoorState();
2405
2406   if (!(door_state & DOOR_NO_COPY_BACK))
2407   {
2408     if (old_door_state & DOOR_OPEN_1)
2409       BlitBitmap(backbuffer, bitmap_db_door,
2410                  DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2411
2412     if (old_door_state & DOOR_OPEN_2)
2413       BlitBitmap(backbuffer, bitmap_db_door,
2414                  VX, VY, VXSIZE, VYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2);
2415
2416     door_state &= ~DOOR_NO_COPY_BACK;
2417   }
2418
2419   return MoveDoor(door_state);
2420 }
2421
2422 unsigned int GetDoorState()
2423 {
2424   return MoveDoor(DOOR_GET_STATE);
2425 }
2426
2427 unsigned int SetDoorState(unsigned int door_state)
2428 {
2429   return MoveDoor(door_state | DOOR_SET_STATE);
2430 }
2431
2432 unsigned int MoveDoor(unsigned int door_state)
2433 {
2434   static int door1 = DOOR_OPEN_1;
2435   static int door2 = DOOR_CLOSE_2;
2436   unsigned long door_delay = 0;
2437   unsigned long door_delay_value;
2438   int stepsize = 1;
2439
2440   if (door_state == DOOR_GET_STATE)
2441     return(door1 | door2);
2442
2443   if (door_state & DOOR_SET_STATE)
2444   {
2445     if (door_state & DOOR_ACTION_1)
2446       door1 = door_state & DOOR_ACTION_1;
2447     if (door_state & DOOR_ACTION_2)
2448       door2 = door_state & DOOR_ACTION_2;
2449
2450     return(door1 | door2);
2451   }
2452
2453   if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
2454     door_state &= ~DOOR_OPEN_1;
2455   else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
2456     door_state &= ~DOOR_CLOSE_1;
2457   if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
2458     door_state &= ~DOOR_OPEN_2;
2459   else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
2460     door_state &= ~DOOR_CLOSE_2;
2461
2462   door_delay_value = (door_state & DOOR_ACTION_1 ? door_1.step_delay :
2463                       door_2.step_delay);
2464
2465   if (setup.quick_doors)
2466   {
2467     stepsize = 20;              /* must be choosen to always draw last frame */
2468     door_delay_value = 0;
2469
2470 #if 0
2471     StopSound(SND_DOOR_OPENING);
2472     StopSound(SND_DOOR_CLOSING);
2473 #endif
2474   }
2475
2476   if (global.autoplay_leveldir)
2477   {
2478     door_state |= DOOR_NO_DELAY;
2479     door_state &= ~DOOR_CLOSE_ALL;
2480   }
2481
2482   if (door_state & DOOR_ACTION)
2483   {
2484     boolean door_1_done = !(door_state & DOOR_ACTION_1);
2485     boolean door_2_done = !(door_state & DOOR_ACTION_2);
2486     int start = ((door_state & DOOR_NO_DELAY) ? DXSIZE : 0);
2487     int end = (door_state & DOOR_ACTION_1 &&
2488                door_1.anim_mode == ANIM_VERTICAL ? DYSIZE : DXSIZE);
2489     int x;
2490
2491     if (!(door_state & DOOR_NO_DELAY) && !setup.quick_doors)
2492     {
2493       /* opening door sound has priority over simultaneously closing door */
2494       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
2495         PlaySoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2496       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
2497         PlaySoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2498     }
2499
2500     for (x = start; x <= end && !(door_1_done && door_2_done); x += stepsize)
2501     {
2502       Bitmap *bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2503       GC gc = bitmap->stored_clip_gc;
2504
2505       if (door_state & DOOR_ACTION_1)
2506       {
2507         int a = MIN(x * door_1.step_offset, end);
2508         int i = (door_state & DOOR_OPEN_1 ? end - a : a);
2509
2510         if (x <= a)
2511         {
2512           BlitBitmap(bitmap_db_door, drawto,
2513                      DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1 + i / 2,
2514                      DXSIZE, DYSIZE - i / 2, DX, DY);
2515
2516           ClearRectangle(drawto, DX, DY + DYSIZE - i / 2, DXSIZE, i / 2);
2517         }
2518
2519         if (door_1.anim_mode == ANIM_HORIZONTAL && x <= DXSIZE)
2520         {
2521           int src1_x = DXSIZE,          src1_y = DOOR_GFX_PAGEY1;
2522           int dst1_x = DX + DXSIZE - i, dst1_y = DY;
2523           int src2_x = DXSIZE - i,      src2_y = DOOR_GFX_PAGEY1;
2524           int dst2_x = DX,              dst2_y = DY;
2525           int width = i, height = DYSIZE;
2526
2527           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
2528           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
2529                            dst1_x, dst1_y);
2530
2531           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
2532           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
2533                            dst2_x, dst2_y);
2534         }
2535         else if (door_1.anim_mode == ANIM_VERTICAL && x <= DYSIZE)
2536         {
2537           int src1_x = DXSIZE,          src1_y = DOOR_GFX_PAGEY1;
2538           int dst1_x = DX,              dst1_y = DY + DYSIZE - i;
2539           int src2_x = 0,               src2_y = DOOR_GFX_PAGEY1 + DYSIZE - i;
2540           int dst2_x = DX,              dst2_y = DY;
2541           int width = DXSIZE, height = i;
2542
2543           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
2544           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
2545                            dst1_x, dst1_y);
2546
2547           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
2548           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
2549                            dst2_x, dst2_y);
2550         }
2551         else if (x <= DXSIZE)   /* ANIM_DEFAULT */
2552         {
2553           int j = (door_1.anim_mode == ANIM_DEFAULT ? (DXSIZE - i) / 3 : 0);
2554
2555           SetClipOrigin(bitmap, gc, DX - i, (DY + j) - DOOR_GFX_PAGEY1);
2556           BlitBitmapMasked(bitmap, drawto,
2557                            DXSIZE, DOOR_GFX_PAGEY1, i, 77,
2558                            DX + DXSIZE - i, DY + j);
2559           BlitBitmapMasked(bitmap, drawto,
2560                            DXSIZE, DOOR_GFX_PAGEY1 + 140, i, 63,
2561                            DX + DXSIZE - i, DY + 140 + j);
2562           SetClipOrigin(bitmap, gc, DX - DXSIZE + i,
2563                         DY - (DOOR_GFX_PAGEY1 + j));
2564           BlitBitmapMasked(bitmap, drawto,
2565                            DXSIZE - i, DOOR_GFX_PAGEY1 + j, i, 77 - j,
2566                            DX, DY);
2567           BlitBitmapMasked(bitmap, drawto,
2568                            DXSIZE-i, DOOR_GFX_PAGEY1 + 140, i, 63,
2569                            DX, DY + 140 - j);
2570
2571           BlitBitmapMasked(bitmap, drawto,
2572                            DXSIZE - i, DOOR_GFX_PAGEY1 + 77, i, 63,
2573                            DX, DY + 77 - j);
2574           BlitBitmapMasked(bitmap, drawto,
2575                            DXSIZE - i, DOOR_GFX_PAGEY1 + 203, i, 77,
2576                            DX, DY + 203 - j);
2577           SetClipOrigin(bitmap, gc, DX - i, (DY + j) - DOOR_GFX_PAGEY1);
2578           BlitBitmapMasked(bitmap, drawto,
2579                            DXSIZE, DOOR_GFX_PAGEY1 + 77, i, 63,
2580                            DX + DXSIZE - i, DY + 77 + j);
2581           BlitBitmapMasked(bitmap, drawto,
2582                            DXSIZE, DOOR_GFX_PAGEY1 + 203, i, 77 - j,
2583                            DX + DXSIZE - i, DY + 203 + j);
2584         }
2585
2586         redraw_mask |= REDRAW_DOOR_1;
2587         door_1_done = (a == end);
2588       }
2589
2590       if (door_state & DOOR_ACTION_2)
2591       {
2592         int a = MIN(x * door_2.step_offset, VXSIZE);
2593         int i = (door_state & DOOR_OPEN_2 ? VXSIZE - a : a);
2594
2595         if (x <= VYSIZE)
2596         {
2597           BlitBitmap(bitmap_db_door, drawto,
2598                      DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2 + i / 2,
2599                      VXSIZE, VYSIZE - i / 2, VX, VY);
2600
2601           ClearRectangle(drawto, VX, VY + VYSIZE - i / 2, VXSIZE, i / 2);
2602         }
2603
2604         if (door_2.anim_mode == ANIM_HORIZONTAL && x <= VXSIZE)
2605         {
2606           int src1_x = VXSIZE,          src1_y = DOOR_GFX_PAGEY2;
2607           int dst1_x = VX + VXSIZE - i, dst1_y = VY;
2608           int src2_x = VXSIZE - i,      src2_y = DOOR_GFX_PAGEY2;
2609           int dst2_x = VX,              dst2_y = VY;
2610           int width = i, height = VYSIZE;
2611
2612           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
2613           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
2614                            dst1_x, dst1_y);
2615
2616           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
2617           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
2618                            dst2_x, dst2_y);
2619         }
2620         else if (door_2.anim_mode == ANIM_VERTICAL && x <= VYSIZE)
2621         {
2622           int src1_x = VXSIZE,          src1_y = DOOR_GFX_PAGEY2;
2623           int dst1_x = VX,              dst1_y = VY + VYSIZE - i;
2624           int src2_x = 0,               src2_y = DOOR_GFX_PAGEY2 + VYSIZE - i;
2625           int dst2_x = VX,              dst2_y = VY;
2626           int width = VXSIZE, height = i;
2627
2628           SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y);
2629           BlitBitmapMasked(bitmap, drawto, src1_x, src1_y, width, height,
2630                            dst1_x, dst1_y);
2631
2632           SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
2633           BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
2634                            dst2_x, dst2_y);
2635         }
2636         else if (x <= VXSIZE)   /* ANIM_DEFAULT */
2637         {
2638           int j = (door_2.anim_mode == ANIM_DEFAULT ? (VXSIZE - i) / 3 : 0);
2639
2640           SetClipOrigin(bitmap, gc, VX - i, (VY + j) - DOOR_GFX_PAGEY2);
2641           BlitBitmapMasked(bitmap, drawto,
2642                            VXSIZE, DOOR_GFX_PAGEY2, i, VYSIZE / 2,
2643                            VX + VXSIZE - i, VY + j);
2644           SetClipOrigin(bitmap, gc,
2645                         VX - VXSIZE + i, VY - (DOOR_GFX_PAGEY2 + j));
2646           BlitBitmapMasked(bitmap, drawto,
2647                            VXSIZE - i, DOOR_GFX_PAGEY2 + j, i, VYSIZE / 2 - j,
2648                            VX, VY);
2649
2650           BlitBitmapMasked(bitmap, drawto,
2651                            VXSIZE - i, DOOR_GFX_PAGEY2 + VYSIZE / 2,
2652                            i, VYSIZE / 2, VX, VY + VYSIZE / 2 - j);
2653           SetClipOrigin(bitmap, gc, VX - i, (VY + j) - DOOR_GFX_PAGEY2);
2654           BlitBitmapMasked(bitmap, drawto,
2655                            VXSIZE, DOOR_GFX_PAGEY2 + VYSIZE / 2,
2656                            i, VYSIZE / 2 - j,
2657                            VX + VXSIZE - i, VY + VYSIZE / 2 + j);
2658         }
2659
2660         redraw_mask |= REDRAW_DOOR_2;
2661         door_2_done = (a == VXSIZE);
2662       }
2663
2664       BackToFront();
2665
2666       if (game_status == GAME_MODE_MAIN)
2667         DoAnimation();
2668
2669       if (!(door_state & DOOR_NO_DELAY))
2670         WaitUntilDelayReached(&door_delay, door_delay_value);
2671     }
2672   }
2673
2674 #if 0
2675   if (setup.quick_doors)
2676   {
2677     StopSound(SND_DOOR_OPENING);
2678     StopSound(SND_DOOR_CLOSING);
2679   }
2680 #endif
2681
2682   if (door_state & DOOR_ACTION_1)
2683     door1 = door_state & DOOR_ACTION_1;
2684   if (door_state & DOOR_ACTION_2)
2685     door2 = door_state & DOOR_ACTION_2;
2686
2687   return (door1 | door2);
2688 }
2689
2690 void DrawSpecialEditorDoor()
2691 {
2692   /* draw bigger toolbox window */
2693   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2694              DOOR_GFX_PAGEX7, 0, EXSIZE + 8, 8,
2695              EX - 4, EY - 12);
2696   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
2697              EX - 4, VY - 4, EXSIZE + 8, EYSIZE - VYSIZE + 4,
2698              EX - 4, EY - 4);
2699
2700   redraw_mask |= REDRAW_ALL;
2701 }
2702
2703 void UndrawSpecialEditorDoor()
2704 {
2705   /* draw normal tape recorder window */
2706   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
2707              EX - 4, EY - 12, EXSIZE + 8, EYSIZE - VYSIZE + 12,
2708              EX - 4, EY - 12);
2709
2710   redraw_mask |= REDRAW_ALL;
2711 }
2712
2713
2714 /* ---------- new tool button stuff ---------------------------------------- */
2715
2716 /* graphic position values for tool buttons */
2717 #define TOOL_BUTTON_YES_XPOS            2
2718 #define TOOL_BUTTON_YES_YPOS            250
2719 #define TOOL_BUTTON_YES_GFX_YPOS        0
2720 #define TOOL_BUTTON_YES_XSIZE           46
2721 #define TOOL_BUTTON_YES_YSIZE           28
2722 #define TOOL_BUTTON_NO_XPOS             52
2723 #define TOOL_BUTTON_NO_YPOS             TOOL_BUTTON_YES_YPOS
2724 #define TOOL_BUTTON_NO_GFX_YPOS         TOOL_BUTTON_YES_GFX_YPOS
2725 #define TOOL_BUTTON_NO_XSIZE            TOOL_BUTTON_YES_XSIZE
2726 #define TOOL_BUTTON_NO_YSIZE            TOOL_BUTTON_YES_YSIZE
2727 #define TOOL_BUTTON_CONFIRM_XPOS        TOOL_BUTTON_YES_XPOS
2728 #define TOOL_BUTTON_CONFIRM_YPOS        TOOL_BUTTON_YES_YPOS
2729 #define TOOL_BUTTON_CONFIRM_GFX_YPOS    30
2730 #define TOOL_BUTTON_CONFIRM_XSIZE       96
2731 #define TOOL_BUTTON_CONFIRM_YSIZE       TOOL_BUTTON_YES_YSIZE
2732 #define TOOL_BUTTON_PLAYER_XSIZE        30
2733 #define TOOL_BUTTON_PLAYER_YSIZE        30
2734 #define TOOL_BUTTON_PLAYER_GFX_XPOS     5
2735 #define TOOL_BUTTON_PLAYER_GFX_YPOS     185
2736 #define TOOL_BUTTON_PLAYER_XPOS         (5 + TOOL_BUTTON_PLAYER_XSIZE / 2)
2737 #define TOOL_BUTTON_PLAYER_YPOS         (215 - TOOL_BUTTON_PLAYER_YSIZE / 2)
2738 #define TOOL_BUTTON_PLAYER1_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2739                                          + 0 * TOOL_BUTTON_PLAYER_XSIZE)
2740 #define TOOL_BUTTON_PLAYER2_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2741                                          + 1 * TOOL_BUTTON_PLAYER_XSIZE)
2742 #define TOOL_BUTTON_PLAYER3_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2743                                          + 0 * TOOL_BUTTON_PLAYER_XSIZE)
2744 #define TOOL_BUTTON_PLAYER4_XPOS        (TOOL_BUTTON_PLAYER_XPOS \
2745                                          + 1 * TOOL_BUTTON_PLAYER_XSIZE)
2746 #define TOOL_BUTTON_PLAYER1_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2747                                          + 0 * TOOL_BUTTON_PLAYER_YSIZE)
2748 #define TOOL_BUTTON_PLAYER2_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2749                                          + 0 * TOOL_BUTTON_PLAYER_YSIZE)
2750 #define TOOL_BUTTON_PLAYER3_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2751                                          + 1 * TOOL_BUTTON_PLAYER_YSIZE)
2752 #define TOOL_BUTTON_PLAYER4_YPOS        (TOOL_BUTTON_PLAYER_YPOS \
2753                                          + 1 * TOOL_BUTTON_PLAYER_YSIZE)
2754
2755 static struct
2756 {
2757   int xpos, ypos;
2758   int x, y;
2759   int width, height;
2760   int gadget_id;
2761   char *infotext;
2762 } toolbutton_info[NUM_TOOL_BUTTONS] =
2763 {
2764   {
2765     TOOL_BUTTON_YES_XPOS,       TOOL_BUTTON_YES_GFX_YPOS,
2766     TOOL_BUTTON_YES_XPOS,       TOOL_BUTTON_YES_YPOS,
2767     TOOL_BUTTON_YES_XSIZE,      TOOL_BUTTON_YES_YSIZE,
2768     TOOL_CTRL_ID_YES,
2769     "yes"
2770   },
2771   {
2772     TOOL_BUTTON_NO_XPOS,        TOOL_BUTTON_NO_GFX_YPOS,
2773     TOOL_BUTTON_NO_XPOS,        TOOL_BUTTON_NO_YPOS,
2774     TOOL_BUTTON_NO_XSIZE,       TOOL_BUTTON_NO_YSIZE,
2775     TOOL_CTRL_ID_NO,
2776     "no"
2777   },
2778   {
2779     TOOL_BUTTON_CONFIRM_XPOS,   TOOL_BUTTON_CONFIRM_GFX_YPOS,
2780     TOOL_BUTTON_CONFIRM_XPOS,   TOOL_BUTTON_CONFIRM_YPOS,
2781     TOOL_BUTTON_CONFIRM_XSIZE,  TOOL_BUTTON_CONFIRM_YSIZE,
2782     TOOL_CTRL_ID_CONFIRM,
2783     "confirm"
2784   },
2785   {
2786     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2787     TOOL_BUTTON_PLAYER1_XPOS,   TOOL_BUTTON_PLAYER1_YPOS,
2788     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2789     TOOL_CTRL_ID_PLAYER_1,
2790     "player 1"
2791   },
2792   {
2793     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2794     TOOL_BUTTON_PLAYER2_XPOS,   TOOL_BUTTON_PLAYER2_YPOS,
2795     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2796     TOOL_CTRL_ID_PLAYER_2,
2797     "player 2"
2798   },
2799   {
2800     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2801     TOOL_BUTTON_PLAYER3_XPOS,   TOOL_BUTTON_PLAYER3_YPOS,
2802     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2803     TOOL_CTRL_ID_PLAYER_3,
2804     "player 3"
2805   },
2806   {
2807     TOOL_BUTTON_PLAYER_GFX_XPOS,TOOL_BUTTON_PLAYER_GFX_YPOS,
2808     TOOL_BUTTON_PLAYER4_XPOS,   TOOL_BUTTON_PLAYER4_YPOS,
2809     TOOL_BUTTON_PLAYER_XSIZE,   TOOL_BUTTON_PLAYER_YSIZE,
2810     TOOL_CTRL_ID_PLAYER_4,
2811     "player 4"
2812   }
2813 };
2814
2815 void CreateToolButtons()
2816 {
2817   int i;
2818
2819   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2820   {
2821     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2822     Bitmap *deco_bitmap = None;
2823     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
2824     struct GadgetInfo *gi;
2825     unsigned long event_mask;
2826     int gd_xoffset, gd_yoffset;
2827     int gd_x1, gd_x2, gd_y;
2828     int id = i;
2829
2830     event_mask = GD_EVENT_RELEASED;
2831
2832     gd_xoffset = toolbutton_info[i].xpos;
2833     gd_yoffset = toolbutton_info[i].ypos;
2834     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
2835     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
2836     gd_y = DOOR_GFX_PAGEY1 + gd_yoffset;
2837
2838     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
2839     {
2840       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
2841
2842       getMiniGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr),
2843                            &deco_bitmap, &deco_x, &deco_y);
2844       deco_xpos = (toolbutton_info[i].width - MINI_TILEX) / 2;
2845       deco_ypos = (toolbutton_info[i].height - MINI_TILEY) / 2;
2846     }
2847
2848     gi = CreateGadget(GDI_CUSTOM_ID, id,
2849                       GDI_INFO_TEXT, toolbutton_info[i].infotext,
2850                       GDI_X, DX + toolbutton_info[i].x,
2851                       GDI_Y, DY + toolbutton_info[i].y,
2852                       GDI_WIDTH, toolbutton_info[i].width,
2853                       GDI_HEIGHT, toolbutton_info[i].height,
2854                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
2855                       GDI_STATE, GD_BUTTON_UNPRESSED,
2856                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y,
2857                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y,
2858                       GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
2859                       GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
2860                       GDI_DECORATION_SIZE, MINI_TILEX, MINI_TILEY,
2861                       GDI_DECORATION_SHIFTING, 1, 1,
2862                       GDI_EVENT_MASK, event_mask,
2863                       GDI_CALLBACK_ACTION, HandleToolButtons,
2864                       GDI_END);
2865
2866     if (gi == NULL)
2867       Error(ERR_EXIT, "cannot create gadget");
2868
2869     tool_gadget[id] = gi;
2870   }
2871 }
2872
2873 void FreeToolButtons()
2874 {
2875   int i;
2876
2877   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2878     FreeGadget(tool_gadget[i]);
2879 }
2880
2881 static void UnmapToolButtons()
2882 {
2883   int i;
2884
2885   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2886     UnmapGadget(tool_gadget[i]);
2887 }
2888
2889 static void HandleToolButtons(struct GadgetInfo *gi)
2890 {
2891   request_gadget_id = gi->custom_id;
2892 }
2893
2894 int get_next_element(int element)
2895 {
2896   switch(element)
2897   {
2898     case EL_QUICKSAND_FILLING:          return EL_QUICKSAND_FULL;
2899     case EL_QUICKSAND_EMPTYING:         return EL_QUICKSAND_EMPTY;
2900     case EL_MAGIC_WALL_FILLING:         return EL_MAGIC_WALL_FULL;
2901     case EL_MAGIC_WALL_EMPTYING:        return EL_MAGIC_WALL_ACTIVE;
2902     case EL_BD_MAGIC_WALL_FILLING:      return EL_BD_MAGIC_WALL_FULL;
2903     case EL_BD_MAGIC_WALL_EMPTYING:     return EL_BD_MAGIC_WALL_ACTIVE;
2904     case EL_AMOEBA_DROPPING:            return EL_AMOEBA_WET;
2905
2906     default:                            return element;
2907   }
2908 }
2909
2910 int el_act_dir2img(int element, int action, int direction)
2911 {
2912   element = GFX_ELEMENT(element);
2913   direction = MV_DIR_BIT(direction);    /* default: MV_NO_MOVING => MV_DOWN */
2914
2915   return element_info[element].direction_graphic[action][direction];
2916 }
2917
2918 static int el_act_dir2crm(int element, int action, int direction)
2919 {
2920   element = GFX_ELEMENT(element);
2921   direction = MV_DIR_BIT(direction);    /* default: MV_NO_MOVING => MV_DOWN */
2922
2923   return element_info[element].direction_crumbled[action][direction];
2924 }
2925
2926 int el_act2img(int element, int action)
2927 {
2928   element = GFX_ELEMENT(element);
2929
2930   return element_info[element].graphic[action];
2931 }
2932
2933 int el_act2crm(int element, int action)
2934 {
2935   element = GFX_ELEMENT(element);
2936
2937   return element_info[element].crumbled[action];
2938 }
2939
2940 int el_dir2img(int element, int direction)
2941 {
2942   element = GFX_ELEMENT(element);
2943
2944   return el_act_dir2img(element, ACTION_DEFAULT, direction);
2945 }
2946
2947 int el2baseimg(int element)
2948 {
2949   return element_info[element].graphic[ACTION_DEFAULT];
2950 }
2951
2952 int el2img(int element)
2953 {
2954   element = GFX_ELEMENT(element);
2955
2956   return element_info[element].graphic[ACTION_DEFAULT];
2957 }
2958
2959 int el2edimg(int element)
2960 {
2961   element = GFX_ELEMENT(element);
2962
2963   return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
2964 }
2965
2966 int el2preimg(int element)
2967 {
2968   element = GFX_ELEMENT(element);
2969
2970   return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
2971 }