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